Python *args e **kwargs
Você já reparou que o print aceita quantos valores você quiser? print("a"), print("a", "b"), print("a", "b", "c", "d"). As funções que você escreveu até aqui não fazem isso: cada uma tem um número fixo de parâmetros. Nesta aula você vai criar funções que recebem quantos argumentos vierem: uma soma de quantos números a Ana quiser, um pedido com quantos itens o cliente pedir, uma ficha com quantos campos forem precisos.
- A receber qualquer quantidade de argumentos posicionais com
*args(eles chegam numa tupla). - A receber qualquer quantidade de argumentos nomeados com
**kwargs(eles chegam num dicionário). - A misturar parâmetros normais,
*argse**kwargs, e em que ordem. - O caminho inverso: abrir uma lista na chamada com
*e um dicionário com**. - Quando usar
*argse quando é melhor passar uma lista. - Os erros reais: a lista inteira caindo no
*args, o parâmetro "engolido", a chave que não veio.
A ideia: a sacola e a ficha com campos extras
No caixa da padaria, a Ana não sabe quantas coisas cada cliente vai levar. Por isso ela não usa uma bandeja com três buracos (um pão, um leite, um café); ela usa uma sacola, onde cabe o que vier: nada, um item, vinte itens. O *args é a sacola: um parâmetro só que recolhe todos os argumentos posicionais que sobrarem.
Já no cadastro de clientes, todo mundo tem nome, mas cada um tem informações a mais diferentes: um deu o telefone, outro o aniversário, outro a cidade. A ficha tem um espaço "outras informações", onde cada coisa vai com uma etiqueta ("telefone: …"). O **kwargs é esse espaço: recolhe todos os argumentos nomeados que sobrarem, cada um com o seu nome.
*args | **kwargs | |
|---|---|---|
| Recolhe | Argumentos posicionais extras: f(1, 2, 3) | Argumentos nomeados extras: f(a=1, b=2) |
| Chegam como | Uma tupla: (1, 2, 3) | Um dicionário: {'a': 1, 'b': 2} |
| Comparação | A sacola | As "outras informações" da ficha, com etiquetas |
| Nome | arguments, "argumentos" | keyword arguments, "argumentos nomeados" |
1. *args: quantos valores vierem
Primeiro, veja o que chega no parâmetro com *:
def mostrar(*numeros):
print(numeros)
mostrar()
mostrar(5)
mostrar(5, 10, 20)
$ python3 ver_args.py () (5,) (5, 10, 20)
- *
- O asterisco antes do nome do parâmetro quer dizer: "junte aqui todos os argumentos posicionais que vierem".
- numeros
- O nome do parâmetro. Escolha um nome que diga o que vem dentro (
numeros,itens,notas). Em exemplos genéricos, o costume é*args; o que importa é o*, e não a palavra "args". - ()
- Sem argumentos, chega uma tupla vazia (aula Tuplas). Não é erro.
- (5,)
- Uma tupla com um item só. A vírgula no fim é o jeito de o Python mostrar que é uma tupla, e não um 5 entre parênteses.
- (5, 10, 20)
- Os três argumentos, na ordem em que vieram.
Como é uma tupla, dá para percorrê-la com for. Uma soma de quantos números a Ana quiser:
def somar(*numeros):
total = 0
for n in numeros:
total = total + n
return total
print(somar(5, 10))
print(somar(5, 10, 20, 30))
print(somar())
$ python3 somar.py 15 65 0
Parâmetros normais antes do *args
O *args pode vir depois de parâmetros comuns. Os primeiros argumentos preenchem os parâmetros comuns, e o resto cai na sacola:
def pedido(cliente, *itens):
print(f"Pedido de {cliente} (itens: {len(itens)})")
for item in itens:
print(f" - {item}")
pedido("Ana", "pão", "leite", "café")
pedido("Bruno", "bolo")
$ python3 pedido.py Pedido de Ana (itens: 3) - pão - leite - café Pedido de Bruno (itens: 1) - bolo
Em pedido("Ana", "pão", "leite", "café"), o "Ana" foi para cliente e os outros três foram juntos para itens.
Missão: a função media só aceita duas notas, e a segunda chamada dá erro. Troque os dois parâmetros por um *notas, para que ela calcule a média de quantas notas vierem. (Dica: sum(notas) soma tudo e len(notas) conta quantas são.)
def media(nota1, nota2):
return (nota1 + nota2) / 2
print(media(8, 9))
print(media(7, 8, 9, 10))
def media(*notas):
return sum(notas) / len(notas)
print(media(8, 9))
print(media(7, 8, 9, 10))
2. **kwargs: quantos nomes vierem
Com dois asteriscos, o parâmetro recolhe os argumentos nomeados (aula Valores Padrão e Argumentos Nomeados):
def ficha(**dados):
print(dados)
ficha(nome="Ana", cidade="Teresina")
ficha()
$ python3 ver_kwargs.py
{'nome': 'Ana', 'cidade': 'Teresina'}
{}
Chegou um dicionário (aula Dicionários): cada nome virou uma chave ('nome', 'cidade') e cada valor ficou ligado à sua chave. Sem argumentos, chega um dicionário vazio, {}. Os itens ficam na ordem em que foram escritos na chamada.
Na ficha de clientes, o nome é obrigatório e o resto é livre:
def ficha(nome, **dados):
print(f"Cliente: {nome}")
for campo, valor in dados.items():
print(f" {campo}: {valor}")
ficha("Ana", cidade="Teresina", telefone="(86) 99999-0000")
ficha("Bruno", aniversario="12/03")
$ python3 ficha.py Cliente: Ana cidade: Teresina telefone: (86) 99999-0000 Cliente: Bruno aniversario: 12/03
- **dados
- Os dois asteriscos: "junte aqui todos os argumentos nomeados que sobrarem". Em exemplos genéricos, o costume é
**kwargs. - dados.items()
- Percorre o dicionário de par em par: a chave em
campoe o valor emvalor(aula Percorrendo um Dicionário).
3. Tudo junto, e a ordem
def pedido(cliente, *itens, **opcoes):
print("cliente:", cliente)
print("itens:", itens)
print("opcoes:", opcoes)
pedido("Ana", "pão", "café", entrega=True, troco=50)
$ python3 tudo.py
cliente: Ana
itens: ('pão', 'café')
opcoes: {'entrega': True, 'troco': 50}
Cada argumento encontrou o seu lugar: o primeiro posicional foi para cliente, os outros posicionais para a tupla itens, e os nomeados para o dicionário opcoes. A ordem no def é sempre esta:
- Parâmetros comuns (
cliente), com ou sem valor padrão. *args(a sacola).- Parâmetros que só podem ser passados com nome (veja o cenário "parâmetro engolido" em Quando dá errado).
**kwargs, sempre por último.
4. O caminho inverso: abrindo a lista na chamada
O asterisco também funciona na chamada, ao contrário: ele abre uma lista (ou tupla) e entrega cada item como um argumento separado. Dois asteriscos abrem um dicionário em argumentos nomeados:
def somar(*numeros):
total = 0
for n in numeros:
total = total + n
return total
def ficha(nome, cidade):
print(f"{nome} mora em {cidade}.")
notas = [8, 9, 7]
print(somar(*notas))
dados = {"nome": "Ana", "cidade": "Teresina"}
ficha(**dados)
compras = ["arroz", "feijão", "café"]
print(compras)
print(*compras)
print(*compras, sep=", ")
$ python3 desempacotar.py 24 Ana mora em Teresina. ['arroz', 'feijão', 'café'] arroz feijão café arroz, feijão, café
| Chamada | É o mesmo que |
|---|---|
somar(*notas) | somar(8, 9, 7) |
ficha(**dados) | ficha(nome="Ana", cidade="Teresina") |
print(compras) | Mostra a lista, com colchetes e aspas |
print(*compras) | print("arroz", "feijão", "café"): três valores, separados por espaço |
Repare: o * no def junta (vários valores viram uma tupla); o * na chamada espalha (uma lista vira vários valores).
Formas diferentes de receber vários valores
Uma função pode receber vários números de dois jeitos: com *args ou com um parâmetro comum que recebe uma lista inteira.
def somar_lista(numeros):
total = 0
for n in numeros:
total = total + n
return total
def somar(*numeros):
total = 0
for n in numeros:
total = total + n
return total
notas = [8, 9, 7]
print(somar_lista(notas))
print(somar(8, 9, 7))
print(somar(*notas))
$ python3 lista_param.py 24 24 24
| Forma | Quando usar |
|---|---|
Parâmetro que recebe uma lista: somar_lista(notas) | Quando os valores já estão numa lista (vieram de um arquivo, de um laço). É o mais comum nos programas |
*args: somar(8, 9, 7) | Quando quem chama vai escrever os valores um por um, na hora, como no print |
**kwargs | Quando a função aceita "informações extras" de nomes variados, que ela só repassa ou mostra |
| Parâmetros fixos com nome | Quando você sabe exatamente o que a função precisa. Prefira sempre que puder: os nomes documentam a função, e erros de digitação viram TypeError na hora |
Quando dá errado
*args
def somar(*numeros):
total = 0
for n in numeros:
total = total + n
return total
notas = [8, 9, 7]
print(somar(notas))
$ python3 lista_inteira.py
Traceback (most recent call last):
File "/home/ana/lista_inteira.py", line 9, in <module>
print(somar(notas))
~~~~~^^^^^^^
File "/home/ana/lista_inteira.py", line 4, in somar
total = total + n
~~~~~~^~~
TypeError: unsupported operand type(s) for +: 'int' and 'list'
Leia o caminho: a linha 9 chamou somar, e dentro dela, na linha 4, a conta falhou. "Tipos não suportados para o +: int e list". Por quê? A lista foi um argumento só, então a sacola ficou com um item: numeros virou ([8, 9, 7],). Na primeira volta do laço, n era a lista inteira, e o Python tentou fazer 0 + [8, 9, 7]. Como consertar: abra a lista na chamada, somar(*notas), ou escreva uma função que recebe a lista num parâmetro comum.
**kwargs
def ficha(**dados):
print(dados)
ficha("Ana")
$ python3 posicional_kwargs.py
Traceback (most recent call last):
File "/home/ana/posicional_kwargs.py", line 5, in <module>
ficha("Ana")
~~~~~^^^^^^^
TypeError: ficha() takes 0 positional arguments but 1 was given
O **dados só recolhe argumentos com nome; ele não tem onde guardar um valor sem etiqueta. Para o Python, a função não aceita nenhum argumento posicional. Como consertar: ficha(nome="Ana"), ou acrescente um parâmetro comum: def ficha(nome, **dados):.
def pedido(*itens, cliente):
print(f"{cliente} pediu {itens}")
pedido("pão", "leite", "Ana")
$ python3 depois_args.py
Traceback (most recent call last):
File "/home/ana/depois_args.py", line 5, in <module>
pedido("pão", "leite", "Ana")
~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
TypeError: pedido() missing 1 required keyword-only argument: 'cliente'
"Falta 1 argumento obrigatório só-nomeado: 'cliente'". A sacola *itens pegou todos os posicionais, inclusive o "Ana", e não sobrou nada para o cliente. Todo parâmetro que vem depois do *args só pode ser preenchido com nome (por isso keyword-only, "só por nome"). Como consertar: passe com nome, ou coloque o parâmetro antes do *itens:
def pedido(*itens, cliente):
print(f"{cliente} pediu {itens}")
pedido("pão", "leite", cliente="Ana")
$ python3 depois_args_ok.py
Ana pediu ('pão', 'leite')
**kwargs antes do *args
def pedido(**opcoes, *itens):
print(itens, opcoes)
$ python3 ordem_def.py
File "/home/ana/ordem_def.py", line 1
def pedido(**opcoes, *itens):
^
SyntaxError: arguments cannot follow var-keyword argument
"Nada pode vir depois do argumento de nomes variáveis" (var-keyword é o nome técnico do **kwargs). Como consertar: o **kwargs é sempre o último: def pedido(*itens, **opcoes):.
def ficha(**dados):
print(dados)
ficha(cor-favorita="azul")
$ python3 nome_invalido.py
File "/home/ana/nome_invalido.py", line 5
ficha(cor-favorita="azul")
^^^^^^^^^^^^^
SyntaxError: expression cannot contain assignment, perhaps you meant "=="?
Os nomes dos argumentos nomeados seguem as regras dos nomes de variáveis (aula Nomes de Variáveis). Com o hífen, o Python leu cor - favorita, uma subtração, e achou estranho um = depois de uma conta. A sugestão "==" não ajuda aqui. Como consertar: cor_favorita="azul".
def ficha(nome, **dados):
print(f"Cliente: {nome}")
print(f"Telefone: {dados['telefone']}")
ficha("Bruno", cidade="Picos")
$ python3 chave_falta.py
Cliente: Bruno
Traceback (most recent call last):
File "/home/ana/chave_falta.py", line 6, in <module>
ficha("Bruno", cidade="Picos")
~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/ana/chave_falta.py", line 3, in ficha
print(f"Telefone: {dados['telefone']}")
~~~~~^^^^^^^^^^^^
KeyError: 'telefone'
KeyError: 'telefone': "erro de chave": o dicionário dados não tem a chave 'telefone', porque o Bruno não informou. Com **kwargs, nada garante que uma chave veio. Como consertar: use o get, que devolve um valor reserva quando a chave não existe:
def ficha(nome, **dados):
print(f"Cliente: {nome}")
print(f"Telefone: {dados.get('telefone', 'não informado')}")
ficha("Bruno", cidade="Picos")
$ python3 chave_get.py Cliente: Bruno Telefone: não informado
Se a função precisa do telefone, o melhor é torná-lo um parâmetro comum: aí, se faltar, o Python avisa na hora com um TypeError claro.
Referência rápida
| Escrita | O que faz |
|---|---|
def f(*args): | Junta os argumentos posicionais numa tupla |
def f(**kwargs): | Junta os argumentos nomeados num dicionário |
def f(a, *args, b, **kwargs): | A ordem: comuns, *args, só-nomeados, **kwargs |
f(*lista) | Abre a lista: cada item vira um argumento posicional |
f(**dicionario) | Abre o dicionário: cada chave vira um argumento nomeado |
print(*lista, sep=", ") | Mostra os itens da lista sem colchetes |
kwargs.get("chave", reserva) | Lê uma chave que talvez não tenha vindo |
| Mensagem | Causa provável |
|---|---|
unsupported operand type(s) for +: 'int' and 'list' | A lista foi inteira para o *args: use f(*lista) |
takes 0 positional arguments but 1 was given | Valor sem nome para uma função que só tem **kwargs |
missing 1 required keyword-only argument | Parâmetro depois do *args, passado sem nome |
arguments cannot follow var-keyword argument | Algo depois do **kwargs no def |
KeyError: 'x' | A chave não veio no **kwargs: use .get |
Exercícios
1. A sacola
Complete o símbolo que faz o parâmetro recolher quantos itens vierem:
print(frutas)
2. As etiquetas
Complete o símbolo que faz o parâmetro recolher os argumentos nomeados:
print(dados)
3. Com def mostrar(*numeros): print(numeros), o que aparece em mostrar(3, 4)?
4. Que tipo de valor chega no parâmetro **opcoes?
5. A lista notas = [8, 9, 7] precisa ir para a função somar(*numeros). Qual chamada funciona?
Missão final: a etiqueta com detalhes
Missão: a função etiqueta não aceita os detalhes do produto, e a chamada dá erro. Acrescente um **detalhes e mostre, embaixo do nome, cada detalhe no formato preco: R$ 18,00.
def etiqueta(produto):
print(f"== {produto} ==")
etiqueta("Bolo de milho", preco="R$ 18,00", peso="800 g")
def etiqueta(produto, **detalhes):
print(f"== {produto} ==")
for nome, valor in detalhes.items():
print(f"{nome}: {valor}")
etiqueta("Bolo de milho", preco="R$ 18,00", peso="800 g")
Depois, acrescente validade="5 dias" na chamada e rode de novo: a etiqueta ganha uma linha, sem mudar nada na função.
Resumo
*argsnodefjunta os argumentos posicionais extras numa tupla; pode chegar vazia.**kwargsjunta os argumentos nomeados extras num dicionário; leia as chaves com.getquando elas podem faltar.- O que importa são os asteriscos; os nomes
argsekwargssão só costume. Prefira nomes que digam o conteúdo. - Ordem no
def: comuns,*args, só-nomeados,**kwargs. Depois do*args, só com nome. - Na chamada, o efeito é o inverso:
f(*lista)espalha a lista em argumentos,f(**dicionario)espalha o dicionário em argumentos nomeados. - Se você sabe exatamente o que a função precisa, parâmetros com nome são mais claros que
*args/**kwargs.