5 de agosto de 2009

Pegadinha: parâmetro default

Toda linguagem tem suas armadilhas e Python não é uma excessão.

A linguagem é prática, clara e objetiva, mas nem tudo é perfeito.

A seção 4.22 do General Python FAQ fala sobre esse assunto, mas passa despercebido para iniciantes na linguagem. É o tipo de informação que quem está começando, não se importa muito. Deixa para aprender depois. Daí vem um problema que é relativamente difícil de resolver, se você não leu com atenção.

Não vou traduzir aqui o que o FAQ diz, mas vou transcrever um trecho e enfatizar o que é dito ali:

It is often expected that a function call creates new objects for default values. This is not what happens. Default values are created exactly once, when the function is defined. If that object is changed, like the dictionary in this example, subsequent calls to the function will refer to this changed object.

By definition, immutable objects such as numbers, strings, tuples, and None, are safe from change. Changes to mutable objects such as dictionaries, lists, and class instances can lead to confusion.

Podemos ler que os valores default não são criados no momento da chamada da função, mas sim no momento da definição. Isso faz toda a diferença. O texto continua explicando o porquê: "Se o objeto é modificado, como o dicionário mostrado no exemplo, as chamadas seguintes à essa função farão referência ao objeto modificado" e não o criará vazio novamente, como seria de esperar (!!).

No parágrafo seguinte, vemos a importância de sabermos quais objetos são mutáveis e imutáveis em Python: números, strings, tuplas e None. Com esses você pode usar os parâmetros default com segurança. Caso contrário, fuja disso.

Quer saber até onde essa situação pode chegar? Veja um exemplo simples abaixo:

# en: A simple test to show how using a mutable object as a default
# parameter can be dangerous!
#
# pt: Um teste simples para mostrar como usar usar um objeto mutável
# como parâmetro default pode ser perigoso!
#
# by Vinicius Assef - August, 5, 2009.

class SimpleTest:
    k = str
    v = str
    def one_more(self, dict={}):
        dict[self.k] = self.v
        return dict

a = SimpleTest()
a.k = 'name'
a.v = 'John Smith'
da = a.one_more()
print da

b = SimpleTest()
b.k = 'phone'
b.v = '888-7766'
db = b.one_more()
print db


Execute no seu shell de Python e veja o retorno! :-o

Eu, por precaução, resolvi não adotar os parâmetros default para nenhum tipo de objeto, mas essa é uma escolha pessoal.

Os anos de estrada me ensinaram a ficar longe das armadilhas. De vez em quando uma delas pode te pegar distraído! ;-)


1 comentários:

  1. Eu normalmente coloco o valor default como None, em seguida, testo se é None, se for, eu atribuo o valor default real.

    ResponderExcluir