Argumentos Opcionais Mutáveis em Python
Marcelo Lino
Posted on February 27, 2023
Argumentos deveriam ser coisas simples, não é mesmo? Você passa um valor para a função e ela faz o que você espera. Mas o que acontece quando a sua função não te obriga a passar um valor e trabalha com um valor padrão?
Uma função de soma que recebe dois valores e retorna a soma deles, porém caso nenhum valor seja inserido os argumentos assumem o valor 10 para cada um, retornando assim 20.
def soma(a=10, b=10):
return a + b
Nesse exemplo os valores padrões são imutáveis, então a cada operação não há modificação no seu conteúdo.
Agora o que acontece se modificamos o valor padrão para uma lista ou um dicionário?
Problema
from random import randint
def mutable_arguments(a_list=[], a_dict={}):
a_list.append(randint(0, 100))
a_dict[randint(0, 100)] = randint(0, 100)
print(f"A lista agora contém os seguintes items: {a_list}")
print(f"O dicionário agora contém os seguintes items: {a_dict}", end="\n\n")
mutable_arguments()
mutable_arguments()
mutable_arguments()
Essa execução gera a seguinte saída:
The local list a_list now contains: [43]
The local dict a_dict now contains: {50: 61}
The local list a_list now contains: [43, 74]
The local dict a_dict now contains: {50: 61, 128: 69}
The local list a_list now contains: [43, 74, 42]
The local dict a_dict now contains: {50: 61, 128: 69, 86: 79}
Note que a cada execução a lista e o dicionário são modificados, isso acontece porque o interpretador do Python aloca cada função ou método na inicialização do programa, caso essa assinatura contenha inicializadores de objetos eles então serão instanciados e alocados em memória durante a inicialização.
Sendo assim, quando você executa seu código e altera esses mesmos valores, está alterando objetos já alocados e não criando novos a cada execução. O que pode ao final do programa gerar resultados inesperados.
Solução
Existem duas formas de resolver esse problema, a primeira é passar None
como valor padrão e então verificar se o valor é None
e então atribuir o valor padrão.
from random import randint
def immutable_arguments(a_list=None, a_dict=None):
if a_list is None:
a_list = []
if a_dict is None:
a_dict = {}
a_list.append(randint(0, 100))
a_dict[randint(0, 100)] = randint(0, 100)
print(f"A lista agora contém os seguintes items: {a_list}")
print(f"O dicionário agora contém os seguintes items: {a_dict}", end="\n\n")
immutable_arguments()
immutable_arguments()
immutable_arguments()
Outra abordagem é reatribuir o valor padrão ou então um objeto mutável vazio.
from random import randint
def immutable_arguments(a_list=None, a_dict=None):
a_list = a_list or []
a_dict = a_dict or {}
a_list.append(randint(0, 100))
a_dict[randint(0, 100)] = randint(0, 100)
print(f"A lista agora contém os seguintes items: {a_list}")
print(f"O dicionário agora contém os seguintes items: {a_dict}", end="\n\n")
immutable_arguments()
immutable_arguments()
immutable_arguments()
Em ambos os casos a saída será a seguinte:
A lista agora contém os seguintes items: [44]
O dicionário agora contém os seguintes items: {7: 37}
A lista agora contém os seguintes items: [39]
O dicionário agora contém os seguintes items: {28: 50}
A lista agora contém os seguintes items: [40]
O dicionário agora contém os seguintes items: {40: 99}
Perceba que agora a cada execução os valores são reatribuídos e não mais modificados.
Posted on February 27, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.