Python Valores Padrão e Argumentos Nomeados
Na aula Parâmetros e Argumentos, cada parâmetro era obrigatório: faltou um argumento, TypeError. Mas na vida real muita coisa tem um "de sempre". Quem pede "um pão de queijo" não precisa dizer "quantidade: um". Nesta aula, os parâmetros ganham um valor padrão, usado quando a chamada não diz nada, e as chamadas ganham nomes: pedido("Café", quantidade=2), que deixam claro o que é cada valor.
- A dar um valor padrão a um parâmetro (
quantidade=1), tornando-o opcional. - A chamar a função com argumentos nomeados (
nome=valor), em qualquer ordem. - A pular um parâmetro opcional e preencher só o seguinte.
- Que o
sepe oenddoprintsão exatamente isso. - A armadilha da lista como valor padrão, e o jeito certo com
None. - As regras de ordem e os erros reais quando elas são quebradas.
A ideia: "o de sempre"
O Seu Joaquim chega à padaria e diz só: "um café". A atendente já sabe o resto: copo médio, com açúcar. É o padrão. Se um dia ele quiser diferente, ele avisa: "um café, sem açúcar". Ele não precisa repetir tudo; diz só o que muda, e diz pelo nome ("sem açúcar"), sem precisar seguir uma ordem.
| Na padaria | No Python |
|---|---|
| O café "de sempre": médio, com açúcar | Um parâmetro com valor padrão: def cafe(tamanho="médio", acucar=True): |
| "Um café" (não diz mais nada) | cafe(): vale o padrão |
| "Um café, sem açúcar" | Um argumento nomeado: cafe(acucar=False) |
1. O valor padrão
def pedido(produto, quantidade=1):
print(f"{quantidade} x {produto}")
pedido("Bolo de milho")
pedido("Pão francês", 10)
$ python3 pedido.py 1 x Bolo de milho 10 x Pão francês
- produto
- Parâmetro obrigatório, como na aula anterior: toda chamada tem de preenchê-lo.
- quantidade=1
- Parâmetro com valor padrão (também se diz "opcional"). Se a chamada não mandar nada para ele, o Python usa o
1. - pedido("Bolo de milho")
- Só um argumento:
produto = "Bolo de milho"equantidadefica com o padrão,1. - pedido("Pão francês", 10)
- Dois argumentos: o
10substitui o padrão.
O valor padrão também funciona com texto:
def saudar(nome, saudacao="Bom dia"):
print(f"{saudacao}, {nome}!")
saudar("Ana")
saudar("Bruno", "Boa noite")
$ python3 saudar.py Bom dia, Ana! Boa noite, Bruno!
=
Nas variáveis, o costume é x = 1, com espaços. No valor padrão (e no argumento nomeado, logo adiante), o costume do PEP 8 é sem espaços: quantidade=1. Com espaços também funciona; é só estilo. Assim, quem lê sabe na hora que aquele = está dentro dos parênteses de uma função.
Missão: a segunda chamada dá erro, porque a Ana não disse a quantidade do bolo. Rode, leia a mensagem e dê ao parâmetro quantidade o valor padrão 1 (mexa só no def).
def pedido(produto, quantidade):
print(f"{quantidade} x {produto}")
pedido("Pão francês", 10)
pedido("Bolo de milho")
def pedido(produto, quantidade=1):
print(f"{quantidade} x {produto}")
pedido("Pão francês", 10)
pedido("Bolo de milho")
2. Argumentos nomeados
Na chamada, você pode dizer a qual parâmetro vai cada valor, escrevendo nome_do_parametro=valor. Com nomes, a ordem não importa mais:
def pedido(produto, quantidade=1):
print(f"{quantidade} x {produto}")
pedido(produto="Café", quantidade=2)
pedido(quantidade=3, produto="Pão de queijo")
pedido("Leite", quantidade=4)
$ python3 nomeados.py 2 x Café 3 x Pão de queijo 4 x Leite
- Na primeira chamada, os dois argumentos têm nome.
- Na segunda, estão na ordem "errada", e mesmo assim deu certo: o Python não olha a posição, olha o nome. Lembra do
30 tem Ana anos.da aula anterior? Com nomes, isso não acontece. - Na terceira, o primeiro vai pela posição e o segundo pelo nome. É o jeito mais comum: o que é óbvio vai pela posição, o que precisa de explicação vai com nome.
Em inglês, argumento nomeado é keyword argument ("argumento por palavra-chave"); o comum, pela posição, é positional argument. As mensagens de erro usam esses dois nomes.
Pulando um parâmetro opcional
O nome é a única saída quando há vários parâmetros opcionais e você quer mudar só o último:
def cartao(nome, mensagem="Parabéns", assinatura="Padaria Pão Quente"):
print(f"{mensagem}, {nome}!")
print(f" Um abraço, {assinatura}")
cartao("Ana")
cartao("Bruno", "Feliz Natal")
cartao("Carla", assinatura="Tia Rosa")
$ python3 cartao.py Parabéns, Ana! Um abraço, Padaria Pão Quente Feliz Natal, Bruno! Um abraço, Padaria Pão Quente Parabéns, Carla! Um abraço, Tia Rosa
No cartão da Carla, a mensagem ficou com o padrão ("Parabéns") e só a assinatura mudou. Pela posição, isso seria impossível: o segundo valor sempre cairia na mensagem.
Você já usou isso: sep e end
O print é uma função com parâmetros de valor padrão: sep=" " (o que vai entre os valores) e end="\n" (o que vai no fim, uma quebra de linha). Na aula As Opções sep e end, você os mudou com argumentos nomeados:
print("Ana", "Bruno", "Carla")
print("Ana", "Bruno", "Carla", sep=" - ")
print("sem pular linha", end="")
print(" (continuou na mesma linha)")
$ python3 print_sep.py Ana Bruno Carla Ana - Bruno - Carla sem pular linha (continuou na mesma linha)
3. As regras de ordem
Duas regras, uma no def e uma na chamada:
| Onde | Regra | Certo | Errado |
|---|---|---|---|
No def | Primeiro os obrigatórios, depois os que têm valor padrão | def pedido(produto, quantidade=1): | def pedido(quantidade=1, produto): |
| Na chamada | Primeiro os posicionais, depois os nomeados | pedido("Café", quantidade=2) | pedido(produto="Café", 2) |
Faz sentido: se um parâmetro opcional viesse antes de um obrigatório, o Python não teria como saber se o primeiro valor da chamada é o opcional ou o obrigatório. As mensagens de quando essas regras são quebradas estão em Quando dá errado.
/ e *
Em algumas funções você vai ver uma / ou um * sozinhos entre os parâmetros. Eles mudam a regra da chamada:
def pedido(produto, *, quantidade=1):— tudo o que vem depois do*só pode ser passado com nome.pedido("Café", quantidade=2)funciona;pedido("Café", 2)dáTypeError: pedido() takes 1 positional argument but 2 were given.def dobro(numero, /):— tudo o que vem antes da/só pode ser passado pela posição.dobro(5)funciona;dobro(numero=5)dáTypeError: dobro() got some positional-only arguments passed as keyword arguments: 'numero'.
Muitas funções embutidas são assim: no help(len) do modo interativo, o len aparece como len(obj, /), e por isso len(obj=[1, 2]) não funciona (TypeError: len() takes no keyword arguments). Para os seus programas, por enquanto, você não precisa deles.
4. A armadilha: lista como valor padrão
A Ana quer uma função que comece uma lista de compras nova para cada pessoa. Parece razoável usar uma lista vazia como padrão:
def nova_lista(item, compras=[]):
compras.append(item)
return compras
ana = nova_lista("arroz")
bruno = nova_lista("café")
print("Ana:", ana)
print("Bruno:", bruno)
$ python3 mutavel.py Ana: ['arroz', 'café'] « o café do Bruno foi parar na lista da Ana Bruno: ['arroz', 'café']
As duas pessoas ficaram com a mesma lista. O arroz da Ana apareceu na lista do Bruno, e o café do Bruno na da Ana. Nenhum erro, só um resultado errado, daqueles que dão muito trabalho para achar.
Por que acontece
O valor padrão é criado uma vez só: no momento em que o Python passa pelo def, e não a cada chamada. A lista [] do padrão é sempre a mesma lista, guardada junto com a função. Cada chamada sem o segundo argumento usa essa mesma lista, e o append (aula Adicionando Itens) vai acumulando nela. Dá para ver isso acontecendo:
def nova_lista(item, compras=[]):
print("A lista padrão antes:", compras)
compras.append(item)
return compras
nova_lista("arroz")
nova_lista("café")
nova_lista("feijão")
print(nova_lista.__defaults__)
$ python3 mutavel_por_que.py A lista padrão antes: [] A lista padrão antes: ['arroz'] A lista padrão antes: ['arroz', 'café'] (['arroz', 'café', 'feijão'],)
A cada chamada, a lista "vazia" do padrão chega com os itens das chamadas anteriores. A última linha espia os valores padrão guardados na função (__defaults__): lá está a lista, com os três itens. Com números e textos isso não acontece, porque eles não podem ser modificados por dentro, como a lista pode com o append (aula Mutável × Imutável).
O jeito certo: None como padrão
def nova_lista(item, compras=None):
if compras is None:
compras = []
compras.append(item)
return compras
ana = nova_lista("arroz")
bruno = nova_lista("café")
print("Ana:", ana)
print("Bruno:", bruno)
print(nova_lista("feijão", ana))
$ python3 mutavel_none.py Ana: ['arroz'] Bruno: ['café'] ['arroz', 'feijão']
- compras=None
- O padrão agora é
None, "nada" (aula O Valor None), que não pode ser modificado. - if compras is None:
- "Se não mandaram lista nenhuma…" (o
isé o jeito certo de comparar comNone, aula is e in). - compras = []
- …cria uma lista vazia nova, agora, dentro desta chamada. Cada chamada ganha a sua.
- nova_lista("feijão", ana)
- Quem quiser continuar uma lista existente ainda pode passá-la.
Nunca use uma lista ([]), um dicionário ({}) ou um conjunto (set()) como valor padrão. Use None e crie o valor novo dentro da função.
Formas diferentes de chamar a mesma função
Com def pedido(produto, quantidade=1):, as chamadas abaixo pedem 2 cafés:
| Chamada | Quando usar |
|---|---|
pedido("Café", 2) | Quando a função é curta e a ordem é óbvia |
pedido("Café", quantidade=2) | O mais legível: o principal pela posição, o detalhe com nome. Prefira quando o valor sozinho não diz o que é (um 2, um True) |
pedido(produto="Café", quantidade=2) | Quando há muitos parâmetros e você quer deixar tudo explícito |
pedido(quantidade=2, produto="Café") | Funciona, mas seguir a ordem do def facilita a leitura |
E quando o valor é o padrão (1 café), o mais simples é não escrever nada: pedido("Café").
Quando dá errado
def pedido(quantidade=1, produto):
print(f"{quantidade} x {produto}")
pedido("Café")
$ python3 ordem_def.py
File "/home/ana/ordem_def.py", line 1
def pedido(quantidade=1, produto):
^^^^^^^
SyntaxError: parameter without a default follows parameter with a default
"Parâmetro sem padrão vem depois de um parâmetro com padrão". O ^^^^^^^ aponta o produto, que é obrigatório e está depois do quantidade=1. Como é SyntaxError, o programa nem começa. Como consertar: obrigatórios primeiro: def pedido(produto, quantidade=1):.
def pedido(produto, quantidade=1):
print(f"{quantidade} x {produto}")
pedido(produto="Café", 2)
$ python3 posicional_depois.py
File "/home/ana/posicional_depois.py", line 5
pedido(produto="Café", 2)
^
SyntaxError: positional argument follows keyword argument
"Argumento posicional vem depois de argumento nomeado". Depois que você começa a usar nomes na chamada, o Python não aceita mais valores soltos, porque não saberia em que posição encaixá-los. Como consertar: pedido("Café", 2) ou pedido(produto="Café", quantidade=2).
def pedido(produto, quantidade=1):
print(f"{quantidade} x {produto}")
pedido("Café", quantidad=2)
$ python3 nome_errado.py
Traceback (most recent call last):
File "/home/ana/nome_errado.py", line 5, in <module>
pedido("Café", quantidad=2)
~~~~~~^^^^^^^^^^^^^^^^^^^^^
TypeError: pedido() got an unexpected keyword argument 'quantidad'. Did you mean 'quantidade'?
"pedido() recebeu um argumento nomeado inesperado, 'quantidad'. Você quis dizer 'quantidade'?". O nome na chamada tem de ser exatamente o nome do parâmetro no def. Como consertar: quantidade=2.
def pedido(produto, quantidade=1):
print(f"{quantidade} x {produto}")
pedido("Café", produto="Leite")
$ python3 repetido.py
Traceback (most recent call last):
File "/home/ana/repetido.py", line 5, in <module>
pedido("Café", produto="Leite")
~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: pedido() got multiple values for argument 'produto'
"pedido() recebeu vários valores para o argumento 'produto'". O "Café", pela posição, já foi para o produto; depois veio produto="Leite" para o mesmo lugar. Como consertar: cada parâmetro recebe um valor só.
A seção 4 inteira é este erro: def f(x, lista=[]): não dá mensagem nenhuma, só mistura os dados de chamadas diferentes. Se uma lista "lembra" de coisas que não deveria, procure um =[] ou ={} num def.
Referência rápida
| Escrita | O que faz |
|---|---|
def f(a, b=10): | b tem valor padrão 10: a chamada pode omiti-lo |
f(1) | a = 1, b = 10 (o padrão) |
f(1, 5) | a = 1, b = 5 (substitui o padrão) |
f(1, b=5) ou f(b=5, a=1) | Argumentos nomeados: a ordem não importa |
def f(x, lista=None): + if lista is None: lista = [] | O jeito certo de ter uma lista "vazia" como padrão |
def f(a, *, b): | b só pode ser passado com nome |
def f(a, /): | a só pode ser passado pela posição |
| Mensagem | Causa provável |
|---|---|
parameter without a default follows parameter with a default | No def, obrigatório depois de opcional |
positional argument follows keyword argument | Na chamada, valor solto depois de um nome=valor |
got an unexpected keyword argument 'x' | Nome de parâmetro digitado errado (ou que não existe) |
got multiple values for argument 'x' | O mesmo parâmetro recebeu pela posição e pelo nome |
| Uma lista "lembra" de chamadas anteriores | Lista como valor padrão: troque por None |
Exercícios
1. O tamanho padrão
Complete para que o café seja "médio" quando ninguém disser o tamanho:
print(f"Café {tamanho}")
2. Só a assinatura
Com a função cartao(nome, mensagem="Parabéns", assinatura="Padaria Pão Quente"), complete a chamada para mudar só a assinatura:
3. Com def pedido(produto, quantidade=1):, o que mostra pedido(quantidade=5, produto="Sonho"), se o corpo é print(f"{quantidade} x {produto}")?
4. Qual destes def está certo?
5. Por que def f(item, lista=[]): é uma armadilha?
Missão final: uma lista para cada um
Missão: rode e veja: a Ana e o Bruno ficaram com a mesma lista de compras. Conserte a função para que cada um tenha a sua (use None como padrão).
def nova_lista(item, compras=[]):
compras.append(item)
return compras
ana = nova_lista("arroz")
bruno = nova_lista("café")
print("Ana:", ana)
print("Bruno:", bruno)
def nova_lista(item, compras=None):
if compras is None:
compras = []
compras.append(item)
return compras
ana = nova_lista("arroz")
bruno = nova_lista("café")
print("Ana:", ana)
print("Bruno:", bruno)
Resumo
def f(a, b=10):dá abum valor padrão: se a chamada não disser nada, vale o 10.- Argumentos nomeados (
f(a=1, b=5)) dizem a qual parâmetro vai cada valor; a ordem deixa de importar, e dá para pular parâmetros opcionais. - No
def: obrigatórios antes dos opcionais. Na chamada: posicionais antes dos nomeados. - O
sepe oenddoprintsão parâmetros com valor padrão. - O valor padrão é criado uma vez, no
def. Por isso nunca use lista ou dicionário como padrão: useNonee crie o valor dentro da função. *e/nodefobrigam a passar com nome ou pela posição; você vai vê-los na documentação.