Se você é desenvolvedor Python, já deve ter visto muitas funções que definem valores padrão para alguns de seus argumentos. Por exemplo:
def power(base, exp=2):
return base**exp
É bem comum usarmos esse artifício quando queremos que determinado parâmetro tenha um valor mesmo que o chamador não tenha passado valor algum para ele. Isso funciona muito bem, porém, pode gerar uma certa confusão quando o tipo do valor default do parâmetro em questão for mutável. Veja o trecho de código abaixo:
def func(valor, seq=[]):
seq.append(valor)
return seq
print func(10)
print func(20)
print func(30)
Antes de executar o código acima, responda: o que será impresso pelo código acima?
Se você respondeu
[10]
[20]
[30]
você está errado, pois o resultado é:
[10]
[10, 20]
[10, 20, 30]
Observe que, na segunda chamada à função func(), a lista seq manteve o valor 10 como elemento e então adicionou o valor 20 ao seu final. Mas por quê, se seq possui uma lista vazia [] como valor default? Este valor não deveria ser atribuído a seq a cada chamada de função?
A resposta é, como tudo em Python, consistente com a linguagem. Em Python, valores default de parâmetros são avaliados somente no momento em que a função estiver sendo avaliada (em sua definição), e não a cada chamada à mesma.
Ao encontrar a palavra-chave def, o interpretador Python avalia a expressão seguinte como uma função e então cria em memória um objeto function referente à função definida. Assim, a atribuição seq=[] é feita pelo interpretador nesse momento, e não a cada vez que func for chamada.
Vamos ver o que acontece: quando func é chamada pela primeira vez, a lista referenciada por seq possui o valor []. Então, dentro da função é feito um append em seq do valor recebido como parâmetro. Como seq é uma referência para um objeto mutável (uma lista), a lista referenciada por seq é quem tem adicionada a si o valor passado como parâmetro. Na chamada seguinte, seq continua apontando para o mesmo objeto lista, que agora possui um elemento (o valor 10) e então o valor 20 será, dentro da função, adicionado ao final da lista referenciada por seq. Na última chamada, é adicionado o valor 30 ao final da lista apontada por seq.
O que deve ser lembrado sempre é que valores default para parâmetros em uma função são avaliados somente na definição da mesma, e não a cada chamada.
Como driblar isso?
Se você precisar que um parâmetro tenha o valor [] quando o chamador não passar valor algum a ele, você pode fazer o seguinte:
def func(valor, seq=None):
if seq is None:
# chamador não forneceu valor para seq
seq = []
seq.append(valor)
return seq
Leia mais
Detalhes como o apresentado neste post são tratadas em alguns livros, como os excelentes:
Obrigado ao Elias Dorneles pela revisão!