Curso de Python / Coletar e desempacotar argumentos com *args e **kwargs
Na definição de uma função, *args reúne argumentos posicionais adicionais numa tupla e **kwargs reúne argumentos nomeados adicionais num dicionário. Numa chamada, * e ** fazem o caminho inverso: desempacotam uma coleção para fornecer argumentos separados à função.
Distinguir coleta na definição e desempacotamento na chamada
args e kwargs são nomes convencionais, não palavras reservadas. A sintaxe com asteriscos determina a função de cada parâmetro; você pode escolher um nome mais específico, como *amounts, para representar vários valores numéricos.
| Local | Expressão | Comportamento |
|---|---|---|
| Definição | def f(*args) |
Reúne os argumentos posicionais adicionais numa tupla. |
| Definição | def f(**kwargs) |
Reúne os argumentos nomeados adicionais num dicionário. |
| Chamada | f(*values) |
Fornece cada item do iterável como um argumento posicional. |
| Chamada | f(**options) |
Fornece as entradas de um mapeamento com chaves de texto como argumentos nomeados. |
“Adicionais” significa que os argumentos ainda não associados aos parâmetros comuns são recolhidos por esses parâmetros variáveis. No exemplo deste artigo, total() não tem um parâmetro posicional comum: todos os argumentos posicionais entram em amounts. O parâmetro fee, escrito depois de *amounts, só pode ser fornecido por nome e tem padrão zero. Esse comportamento se conecta às regras de argumentos posicionais, nomeados e valores padrão.
Executar uma soma variável e descrever opções
Salve o programa completo em varargs.py, com codificação UTF-8, numa pasta de prática. No terminal com o ambiente virtual do projeto selecionado, execute python varargs.py. Em macOS ou Ubuntu sem ambiente virtual, use python3 varargs.py quando esse for o nome do interpretador. Não há pacotes externos para instalar.
def total(*amounts, fee=0):
return sum(amounts) + fee
def describe(**fields):
return ", ".join(f"{key}={value}" for key, value in sorted(fields.items()))
values = [3500, 8000, 2500]
print(total(*values, fee=500))
options = {"category": "food", "currency": "KRW"}
print(describe(**options))
A saída esperada é:
14500
category=food, currency=KRW
total(*values, fee=500) fornece 3500, 8000 e 2500 como três argumentos posicionais. Eles formam a tupla amounts; a soma é 14000 e a taxa adicional eleva o resultado para 14500. Já describe(**options) transforma as entradas do dicionário em argumentos nomeados, que são recolhidos por fields.
Dentro de describe(), sorted(fields.items()) ordena as entradas por chave. A expressão produz trechos como category=food, e join() os une com vírgula e espaço. Por isso a saída segue a ordem alfabética das chaves, mesmo se a ordem de inserção em options for invertida. Os textos indicam categoria “alimentação” e moeda KRW; são rótulos e não provocam conversão monetária ou outra ação externa.
Reconhecer duplicações e expansões não pretendidas
As chaves de um mapeamento expandido com ** numa chamada precisam ser strings. A função de destino também precisa aceitar esses nomes, seja como parâmetros explícitos, seja por um parâmetro **kwargs. Como describe() aceita qualquer nome, um erro de digitação como curreny não gera uma exceção: ele aparece na descrição. Aceitar nomes não equivale a validar seu significado.
A chamada describe(category="food", **{"category": "travel"}) causa TypeError, pois a mesma chave foi fornecida duas vezes, mesmo com **fields na definição. Isso difere da criação de um dicionário como {**{"category": "food"}, **{"category": "travel"}}, em que o último valor substitui o anterior. Ao receber um erro de duplicação, confira tanto os nomes explícitos como os mapeamentos expandidos.
Também é necessário distinguir uma coleção de seus itens. total(values) fornece uma lista como um único argumento; neste programa, sum(amounts) tentaria somar essa lista a um número e causaria TypeError. total(*"123") fornece os textos "1", "2" e "3", sem convertê-los em números, e também falha. Desempacotar um dicionário com apenas * fornece suas chaves, não seus valores.
Uma função intermediária, ou wrapper, pode receber *args e **kwargs e encaminhar a chamada com func(*args, **kwargs). Passar kwargs sem os dois asteriscos enviaria o dicionário inteiro como um argumento posicional. O destino continua aplicando suas próprias regras; o encaminhamento não corrige nomes desconhecidos, tipos inadequados ou parâmetros duplicados. A referência de chamadas de funções detalha essas verificações.
Testar limites e manter a interface compreensível
- Acrescente somente
0ao fim devalues. Execute novamente: a soma com a taxa continua sendo 14500. Restaure a lista original antes do teste seguinte. - Na primeira impressão, troque a chamada por
total(*values). Semfee=500, aplica-se o padrão zero e a soma passa a 14000. Restaure a taxa explícita. - Troque temporariamente a primeira chamada por
total(). Uma tupla de valores vazia tem soma zero, portanto o resultado é 0. Comtotal(fee=500), o resultado seria 500: a função não exige pelo menos um valor. - Teste separadamente a duplicação de
categorymostrada acima e confirmeTypeError. Retire a chamada inválida, restaure o programa e compare novamente as duas linhas de saída originais.
Argumentos variáveis são úteis quando a quantidade de entradas faz parte do problema. Se a função sempre recebe uma coleção com um significado único, um parâmetro como amounts que recebe a própria lista pode tornar a interface mais explícita. Da mesma forma, nomes fixos para opções obrigatórias permitem identificar omissões e erros mais cedo.
Ao encaminhar opções para uma API externa, aceite somente os nomes permitidos e verifique os campos obrigatórios e os valores antes do envio. Em operações de banco de dados, desempacotar argumentos não substitui consultas parametrizadas. O mecanismo de coleta e expansão é descrito no tutorial de listas de argumentos arbitrárias e desempacotamento; a validação do contrato continua sendo responsabilidade do programa.




