Módulo 3 - Funções avançadas
A armadilha do padrão mutável
11 min de leitura · por Cesar Gargiulo, revisado pela equipe ValorFinal e GuardiaSec · Atualizado em 01/07/2026
O que você vai aprender
- Reproduzir o bug do argumento padrão mutável e entender a causa.
- Compreender que o valor padrão é criado uma vez, na definição.
- Aplicar a solução com None para criar um novo objeto a cada chamada.
- Reconhecer o padrão de correção em qualquer função que aceite lista ou dicionário.
Ouvir o resumo desta aula
Um recap de cerca de 2 minutos na voz do Valim, para ouvir no trânsito ou na academia.
Ler a transcrição do resumo
Resumo da aula: A armadilha do padrão mutável.
Os objetivos desta aula. Reproduzir o bug do argumento padrão mutável e entender a causa. Compreender que o valor padrão é criado uma vez, na definição. Aplicar a solução com None para criar um novo objeto a cada chamada. Reconhecer o padrão de correção em qualquer função que aceite lista ou dicionário.
Veja o essencial, parte por parte.
O bug clássico, ao vivo. O valor padrão é criado uma única vez, quando a função é definida, não a cada chamada.
Por que isso acontece. Nunca use [] , {} ou set() como valor padrão de um parâmetro.
A solução: None como sentinela. A correção é simples e vira reflexo com o tempo.
Esse foi o resumo do essencial. Para se aprofundar, leia a aula completa e responda os exercícios.
O bug clássico, ao vivo
Este é, talvez, o erro mais famoso de quem passa do básico para o intermediário em Python. A intenção é boa: você quer uma função que receba uma lista, e, se ninguém passar, comece com uma lista vazia. Parece natural escrever lista=[] como padrão. O problema é sutil e cruel: esse [] é avaliado uma única vez, no momento em que a função é definida, e o mesmo objeto vazio passa a ser reaproveitado em toda chamada que não informar a lista. Como listas são mutáveis, o que uma chamada acrescenta fica lá para a próxima.
# Errado: lista mutavel como valor padrao
def adicionar(item, lista=[]):
lista.append(item)
return lista
print(adicionar("a")) # ['a']
print(adicionar("b")) # ['a', 'b'] <- deveria ser so ['b']!
print(adicionar("c")) # ['a', 'b', 'c'] <- o lixo se acumula
# A mesma lista e reaproveitada entre chamadas: bug silencioso.O padrão [] é criado uma vez e compartilhado: cada chamada herda o que a anterior deixou.
Leia a saída com atenção. A segunda chamada deveria devolver apenas a letra b, porque não passamos nenhuma lista. Em vez disso, ela devolve a e b, porque reaproveitou a mesma lista da primeira chamada. Na terceira, o acúmulo continua. O código não deu erro nenhum; ele simplesmente fez algo diferente do que você imaginou. Esse tipo de bug silencioso é o pior, porque passa nos testes rápidos e explode mais tarde, em produção, com dados estranhos que ninguém entende de onde vieram.
Por que isso acontece
A raiz do problema é uma regra do Python que você viu na primeira aula deste módulo: o valor padrão é calculado uma única vez, quando a definição da função é lida, e não a cada chamada. Para um número ou um texto isso não faz diferença, porque eles são imutáveis: ninguém consegue alterar o número 5 ou o texto vazio por dentro. Já uma lista é mutável. O append muda o próprio objeto, e como esse objeto é sempre o mesmo padrão criado lá no começo, a mudança persiste entre chamadas.
Padrão imutável (seguro)
- saudacao="Olá", contador=0, ativo=True
- O valor não pode ser alterado por dentro
- Nada vaza de uma chamada para a outra
- Pode usar direto, sem cuidado extra
Padrão mutável (armadilha)
- lista=[], config={}, itens=set()
- O objeto é alterado por append, update, add
- O mesmo objeto é compartilhado entre chamadas
- Precisa da solução com None
A solução: None como sentinela
A correção é simples e vira reflexo com o tempo. Em vez de usar a lista vazia como padrão, use None, que é imutável e serve de sinal de que nada foi passado. Dentro da função, um if verifica: se o argumento é None, crie uma lista nova ali mesmo. Como a criação passa a acontecer a cada chamada, cada uma ganha a sua própria lista, e o vazamento acaba. Esse padrão, chamado de sentinela None, funciona para listas, dicionários, conjuntos e qualquer outro objeto mutável.
# Certo: None como padrao, cria a lista dentro
def adicionar(item, lista=None):
if lista is None:
lista = []
lista.append(item)
return lista
print(adicionar("a")) # ['a']
print(adicionar("b")) # ['b'] <- isolado, como esperado
print(adicionar("c")) # ['c'] <- cada chamada tem sua listaCom None e o if, cada chamada cria a própria lista. O bug desaparece.
Compare as duas saídas lado a lado: agora cada chamada devolve só o seu item, sem herdar nada da anterior. A diferença é uma linha, o if lista is None, mas ela muda tudo. Use is None, com o operador is, e não == None: é a forma idiomática de comparar com None em Python. Guardando esse padrãozinho, você atravessa toda a carreira sem cair na armadilha mais clássica da linguagem, aquela que quase todo programador Python já encontrou pelo menos uma vez.
Teste rápido
Por que usar lista=None em vez de lista=[] como valor padrão?
Perguntas frequentes
- Esse problema acontece só com listas?
- Não. Acontece com qualquer objeto mutável usado como padrão: listas, dicionários, conjuntos e até objetos das suas próprias classes. Todos são criados uma vez e compartilhados entre chamadas. Números, textos, tuplas, True, False e None são imutáveis e não causam o problema, por isso são padrões seguros.
- Por que usar is None e não == None?
- None é único no programa, então a comparação de identidade com is é a forma idiomática e recomendada em Python, inclusive pela PEP 8. Além de mais correta em intenção, evita surpresas com objetos que redefinem o operador de igualdade. Escreva sempre if x is None e if x is not None.
- Dá para ver que a lista padrão é sempre a mesma?
- Dá. Você pode inspecionar adicionar.__defaults__, que mostra os valores padrão guardados na função. Depois de algumas chamadas com o código errado, você verá que a lista lá dentro já não está vazia, prova de que é sempre o mesmo objeto sendo reaproveitado.
- E se eu realmente quiser manter estado entre chamadas?
- Aí o certo é ser explícito, não depender de um efeito colateral obscuro do padrão mutável. Use uma variável fora da função, um argumento que você mesmo passa, ou uma classe que guarde o estado (módulo 10). Manter estado por acidente, via padrão mutável, é fonte de bug, não uma técnica.
- O editor ou o Python me avisam desse erro?
- O Python não dá erro, porque o código é válido. Mas ferramentas de análise, como linters (por exemplo, o Ruff ou o Pylint), sinalizam argumento padrão mutável com um aviso específico. Vale rodar um linter no projeto justamente para pegar armadilhas silenciosas como esta antes que virem bug.
- Preciso do if None mesmo quando quase sempre passo a lista?
- Sim, sempre que o parâmetro tiver padrão mutável. Basta uma única chamada sem informar a lista para o compartilhamento começar. O custo do if lista is None é uma linha; o custo de esquecê-lo é um bug intermitente e difícil de rastrear. É barato fazer certo desde o início.
Fontes
Seu progresso fica salvo neste aparelho. Assinantes sincronizam entre os aparelhos.