Argumentos Opcionais Mutáveis em Python
Tags: python, beginners, programming, tutorial
Published: 2023-02-27 18:30:18

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.

```python
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

```python
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:

```text
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.

```python
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.

```python
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:

```text
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.