Caderno de campo DoesItHold

Parâmetros mutáveis preservam estado entre chamadas Python

Um argumento padrão é avaliado quando a função é definida; a mesma lista pode ser reutilizada.

DoesItHold Editorial · · revisado em · guide-python-defaults-001

Resposta direta

Use None como sentinela e aloque o valor mutável dentro da função.

O Python avalia expressões padrão uma vez, quando a instrução def é executada. Uma lista, um dicionário ou um conjunto criado ali passa a fazer parte do objeto da função e é reutilizado sempre que a chamada omite o argumento. Alterá-lo muda o que a próxima chamada recebe.

Esse estado surpreendente é determinístico, não aleatório. Reproduza com duas chamadas que omitem o argumento e compare os valores retornados e suas identidades. O experimento separa o padrão compartilhado de um estado armazenado em outro lugar.

Rastreie a falha
  1. 01
    Definiçãouma lista padrão é alocada
  2. 02
    Chamada Aa lista compartilhada vira ['a']
  3. 03
    Chamada Ba mesma lista vira ['a', 'b']
  4. 04
    SentinelaNone cria uma lista por chamada
O defeito é a reutilização do objeto: os dois argumentos omitidos apontam para a lista criada na execução de def. A sentinela move a alocação para cada chamada.

Exemplo mínimo

Região de código com rolagem

Com defeito
def collect(value, items=[]):
    items.append(value)
    return items
Corrigido
def collect(value, items=None):
    if items is None:
        items = []
    items.append(value)
    return items

O que muda: Duas chamadas que omitem o argumento deixam de compartilhar um objeto; uma coleção fornecida explicitamente continua sendo alterada como solicitado.

Método de depuração

Chame a função duas vezes sem o argumento, compare identidades e mova a alocação para o fluxo da chamada.

  1. Reproduza com duas chamadas que omitem o argumento mutável.
  2. Inspecione o padrão por __defaults__ e compare as identidades dos objetos.
  3. Troque o padrão mutável por None e aloque somente quando o argumento for None.
  4. Teste tanto a omissão quanto uma coleção vazia fornecida explicitamente.

Correção incorreta comum

Copiar a lista no return ainda permite mutação durante a chamada e esconde a propriedade do objeto. Usar items = items or [] também substitui uma lista vazia válida fornecida por quem chamou.

Limites e casos especiais

Um padrão mutável persistente pode ser intencional para cache, mas esse estado fica implícito, compartilhado e difícil de reiniciar. Prefira um objeto de cache explícito ou um decorador padrão para deixar propriedade e ciclo de vida visíveis.

Fontes