✅ Artigo atualizado em Setembro 2026 Novas seções sobre o que significa
**(potência ou desempacotamento),*e**na chamada, juntar dicionários, parâmetros/e*, repasse de argumentos em wrappers, erros comuns com traceback e 8 exercícios resolvidos. Todos os exemplos foram executados e as saídas conferidas.
Resposta rápida
*args e **kwargs deixam uma função receber quantos argumentos você quiser: *args junta os posicionais extras numa tupla e **kwargs junta os nomeados extras num dicionário. Na chamada da função, * e ** fazem o caminho inverso: desempacotam listas e dicionários em argumentos.
1
2
3
4
5
def f(*args, **kwargs):
print(args, kwargs)
f(1, 2, nome="Ana") # (1, 2) {'nome': 'Ana'}
f(*[3, 4], **{"x": 5}) # (3, 4) {'x': 5}
Resumo em 30 segundos:
- Os nomes
argsekwargssão convenção: o que importa é o*e o**. - Ordem na definição: posicionais,
*args, keyword-only,**kwargs. -
argsé uma tupla (imutável) ekwargsé umdict. - Entre dois números,
**é potência (2 ** 3dá 8); antes de um dicionário, é desempacotamento. -
{**a, **b}junta dicionários; em chave repetida, vence o último.
Salve salve querido Pythonista!
*args e **kwargs permitem número variável de argumentos em funções! Essenciais para decorators, wrappers e APIs flexíveis.
Neste guia, você vai aprender:
- ✅
*args- Argumentos posicionais variáveis - ✅
**kwargs- Argumentos nomeados variáveis - ✅ O que significa
**- potência, kwargs ou desempacotamento - ✅ Ordem de parâmetros - positional,
*args, keyword,**kwargs(e os marcadores/e*) - ✅ Casos práticos - Decorators, loggers, wrappers
- ✅ Erros comuns e exercícios resolvidos - para fixar e treinar para a prova
Ambos são geralmente utilizados no contexto da criação de Funções em Python. Se ainda não domina Funções, clique aqui e veja nosso post completo sobre Funções no Python
Agora vamos desvendar o que essas duas palavrinhas querem dizer em Python! ![]()
Vá Direto ao Assunto…
*argse**kwargs- O que significa
**em Python - Argumentos não nomeados
*args - Argumentos nomeados
**kwargs - Desempacotar argumentos com
*e**na chamada - Casos Práticos
- Ordem de Parâmetros
- Parâmetros só-posicionais (
/) e só-nomeados (*) - Erros comuns
- Exercícios resolvidos
*args e **kwargs
*args e **kwargs permitem que você passe um número não especificado de argumentos para uma função.
Dessa forma, ao escrever uma função, você não precisa definir quantos argumentos serão passados para sua função.
Observação importante: Escrever *args e **kwargs é apenas uma convenção: *args vem do inglês “arguments” (argumentos) e **kwargs vem do inglês “keyword arguments” (argumentos nomeados). Podemos, por exemplo, utilizar *banana e **bananas se quisermos ![]()
A documentação oficial explica esses recursos na seção Mais sobre definição de funções do tutorial do Python.
O que significa ** em Python
Quem busca “o que significa ** em Python” costuma cair em uma de duas coisas bem diferentes: o operador de potência e o desempacotamento de dicionários. O Python decide pelo lugar onde o ** aparece:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
print(2 ** 3) # 8 (potência)
def mostrar(**kwargs):
print(kwargs)
mostrar(a=1, b=2) # {'a': 1, 'b': 2}
dados = {"a": 1, "b": 2}
mostrar(**dados) # {'a': 1, 'b': 2}
print({**dados, "c": 3}) # {'a': 1, 'b': 2, 'c': 3}
x = 5
x **= 2
print(x) # 25
Onde o ** aparece |
Exemplo | O que faz |
|---|---|---|
| Entre dois números | 2 ** 3 |
Potência: 2 elevado a 3, resultado 8
|
| Com atribuição | x **= 2 |
Potência e atribuição: o mesmo que x = x ** 2
|
| Na definição da função | def f(**kwargs) |
Junta os argumentos nomeados extras num dict
|
| Na chamada da função | f(**dados) |
Desempacota um dict em argumentos nomeados |
Dentro de {}
|
{**a, **b} |
Copia os pares de um dict para outro (junta dicionários) |
Com o * simples acontece a mesma coisa: é multiplicação entre números, mas vira *args na definição, desempacotamento de lista ou tupla na chamada (f(*lista)) e, sozinho na lista de parâmetros, marca os parâmetros só-nomeados.
Onde o * aparece |
Exemplo | O que faz |
|---|---|---|
| Entre dois números | 3 * 4 |
Multiplicação, resultado 12
|
| Na definição da função | def f(*args) |
Junta os argumentos posicionais extras numa tupla |
| Na chamada da função | f(*lista) |
Desempacota uma lista ou tupla em argumentos posicionais |
Dentro de [] ou ()
|
[*a, *b] |
Junta sequências numa nova lista ou tupla |
| Numa atribuição | primeiro, *resto = lista |
Guarda “o resto” numa lista |
| Sozinho nos parâmetros | def f(a, *, b) |
Obriga a passar b pelo nome |
Se você veio pelo ** de potência, a explicação completa (com precedência e exemplos com números negativos) está na seção o que faz ** em Python no post de operadores aritméticos. Aqui a gente segue com o ** de funções.
Argumentos não nomeados *args
Vamos dar uma olhada em *args primeiro!
O *args é usado para receber uma quantidade variável de argumentos sem a palavra-chave (keyword) na função. Dentro da função, args é uma tupla com esses valores (saiba mais sobre funções e parâmetros no Python).
Aqui está um exemplo pra facilitar o entendimento:
1
2
3
4
5
6
7
def função_com_argumentos_variaveis(arg, *args):
print("Primeiro argumento:", arg)
for argumento in args:
print("Argumento de *args:", argumento)
função_com_argumentos_variaveis('primeiro_arg', 'arg2', 'arg3', 'arg3')
Veja a saída:
1
2
3
4
Primeiro argumento: primeiro_arg
Argumento de *args: arg2
Argumento de *args: arg3
Argumento de *args: arg3
Fique atento a ordem!
Argumentos não nomeados (*args) vem sempre primeiro que os argumentos nomeados (**kwargs). Na chamada da função vale a mesma regra: argumentos posicionais antes dos nomeados.
Veja a seguinte chamada de função:
1
função_com_argumentos_variaveis(arg='arg', 'arg2', 'arg3', 'arg3')
O seguinte erro será lançado:
1
2
File "<stdin>", line 1
SyntaxError: positional argument follows keyword argument
Agora vamos a um exemplo mais útil!
Uma função que soma os argumentos passados, independente do número de argumentos que seja passado, veja:
1
2
3
4
5
6
7
8
9
10
11
def adicao(*args):
resultado = 0
for argumento in args:
resultado += argumento
return resultado
print(adicao(1, 2))
print(adicao(1, 2, 4, 6))
print(adicao(1, 2, 4, 6, 8, 10))
Resultando em:
1
2
3
3
13
31
Estou construindo o Ebookr.ai, uma plataforma onde você cria ebooks profissionais com IA sobre qualquer assunto - do zero ao PDF pronto, com capas e infográficos gerados automaticamente. Dá uma olhada!
Argumentos nomeados **kwargs
O **kwargs possibilita verificarmos os parâmetros nomeados da função, isto é, aqueles parâmetros que são passados com um nome!
Eles estarão disponíveis como um dicionário ({'chave': 'valor'}) dentro da função, então tudo o que você já sabe sobre dicionários no Python (.items(), .get(), in) funciona com kwargs.
Vamos ao exemplo:
1
2
3
4
5
6
7
8
9
10
11
def concatena (**kwargs):
print(f'Valores recebidos: {kwargs}')
resultado = ""
for valor in kwargs.values():
resultado += f'{valor} '
return resultado
print(concatena(a="Python", b="Academy", c="Rules!"))
print()
print(concatena(a="Python", b="é", c="na", d="Python", e="Academy!"))
Veja a saída, perceba que recebemos um dict na variável kwargs:
1
2
3
4
5
Valores recebidos: {'a': 'Python', 'b': 'Academy', 'c': 'Rules!'}
Python Academy Rules!
Valores recebidos: {'a': 'Python', 'b': 'é', 'c': 'na', 'd': 'Python', 'e': 'Academy!'}
Python é na Python Academy!
Para melhor entender vamos juntar tudo e printar o que vem em cada um dos tipos de parâmetros.
Veja o exemplo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def funcao(arg_1, arg_2, *args, arg_kw, **kwargs):
print(f'Parâmetro 1: {arg_1}')
print(f'Parâmetro 2: {arg_2}')
print(f'*args: {args}')
print(f'Argumento nomeado: {arg_kw}')
print(f'**kwargs: {kwargs}')
# Chamada da Função
funcao(
1, # Parâmetro 1
'a', # Parâmetro 2
'arg1', # *args
'arg2', # *args
2, # *args
arg_kw='KW',# arg_kw
banana='B', # **kwargs
bananas='A' # **kwargs
)
A saída deste código será:
1
2
3
4
5
Parâmetro 1: 1
Parâmetro 2: a
*args: ('arg1', 'arg2', 2)
Argumento nomeado: KW
**kwargs: {'banana': 'B', 'bananas': 'A'}
Está curtindo esse conteúdo? ![]()
Que tal receber 30 dias de conteúdo direto na sua Caixa de Entrada?
Desempacotar argumentos com * e ** na chamada
Até aqui o * e o ** apareceram na definição da função, juntando argumentos. Na chamada eles fazem o contrário: pegam uma coleção pronta e espalham os valores como argumentos separados. A documentação chama isso de desempacotando listas de argumentos.
1
2
3
4
5
6
7
8
def cadastrar(nome, idade, cidade):
print(f"{nome}, {idade} anos, mora em {cidade}")
pessoa_lista = ["Ana", 30, "Recife"]
pessoa_dict = {"nome": "Bia", "cidade": "Natal", "idade": 25}
cadastrar(*pessoa_lista) # Ana, 30 anos, mora em Recife
cadastrar(**pessoa_dict) # Bia, 25 anos, mora em Natal
Repare em duas regras:
- Com
*, a ordem importa: o primeiro item vai para o primeiro parâmetro, e assim por diante. - Com
**, a ordem não importa, mas as chaves precisam ter exatamente os nomes dos parâmetros. Uma chave a mais geraTypeError: ... got an unexpected keyword argument(veja em Erros comuns).
O * funciona com qualquer iterável (lista, tupla, range, string) e é muito usado com funções nativas:
1
2
3
4
5
6
numeros = [3, 1, 2]
print(*numeros) # 3 1 2
print(*numeros, sep=", ") # 3, 1, 2
print(max(*numeros)) # 3
print(*"abc") # a b c
print(list(range(*[1, 10, 3]))) # [1, 4, 7]
Juntar dicionários com **
Dentro de chaves {}, o ** copia os pares de um dicionário para um dicionário novo. É o jeito mais comum de juntar configurações padrão com as escolhas do usuário:
1
2
3
4
5
6
7
8
9
10
11
padrao = {"tema": "claro", "idioma": "pt", "fonte": 14}
usuario = {"tema": "escuro", "fonte": 16}
config = {**padrao, **usuario}
print(config) # {'tema': 'escuro', 'idioma': 'pt', 'fonte': 16}
# A ordem decide quem vence em chave repetida
print({**usuario, **padrao}) # {'tema': 'claro', 'fonte': 14, 'idioma': 'pt'}
# Python 3.9+: operador de união, mesmo resultado
print(padrao | usuario) # {'tema': 'escuro', 'idioma': 'pt', 'fonte': 16}
Nenhum dos dois dicionários originais é alterado: {**a, **b} sempre cria um dicionário novo, e em chave repetida vence o último.
Juntar listas e tuplas com *
O mesmo vale para sequências com um asterisco só, e também para “sobras” numa atribuição:
1
2
3
4
5
6
a = [1, 2]
b = (3, 4)
print([*a, *b, 5]) # [1, 2, 3, 4, 5]
primeiro, *resto = [10, 20, 30, 40]
print(primeiro, resto) # 10 [20, 30, 40]
Atenção: numa atribuição, *resto vira lista; já nos parâmetros de uma função, *args vira tupla.
Casos Práticos
1. Logger Flexível
1
2
3
4
5
6
7
8
def log(level, message, **metadata):
print(f"[{level}] {message}")
if metadata:
print(f"Metadata: {metadata}")
log("INFO", "Usuário logou")
log("ERROR", "Falha no banco", user_id=123, ip="192.168.1.1")
log("DEBUG", "Query executada", query="SELECT *", duration=0.5)
Saída:
1
2
3
4
5
[INFO] Usuário logou
[ERROR] Falha no banco
Metadata: {'user_id': 123, 'ip': '192.168.1.1'}
[DEBUG] Query executada
Metadata: {'query': 'SELECT *', 'duration': 0.5}
2. Decorator Genérico
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import time
def timer(func):
def wrapper(*args, **kwargs): # Aceita qualquer argumento!
start = time.time()
result = func(*args, **kwargs) # Passa adiante
duration = time.time() - start
print(f"{func.__name__} levou {duration:.2f}s")
return result
return wrapper
@timer
def processar(n, verbose=False):
time.sleep(n)
if verbose:
print(f"Processado {n}s")
processar(1)
processar(2, verbose=True)
Saída (os tempos podem variar alguns centésimos):
1
2
3
processar levou 1.00s
Processado 2s
processar levou 2.00s
O wrapper não sabe quais argumentos processar espera, e nem precisa: ele recebe tudo em *args e **kwargs e repassa igual. Se decorators ainda são novidade, veja o guia completo de decorators em Python.
3. Criar Objeto Dinâmico
1
2
3
4
5
6
7
8
9
10
11
12
class Config:
def __init__(self, **settings):
for key, value in settings.items():
setattr(self, key, value)
def __repr__(self):
attrs = ', '.join(f"{k}={v}" for k, v in self.__dict__.items())
return f"Config({attrs})"
config = Config(host="localhost", port=8000, debug=True)
print(config.host) # localhost
print(config) # Config(host=localhost, port=8000, debug=True)
4. Combinar Dicionários
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def merge_configs(*configs, override=True):
result = {}
for config in configs:
if override:
result.update(config) # Último sobrescreve
else:
for k, v in config.items():
result.setdefault(k, v) # Primeiro vence
return result
default = {'host': 'localhost', 'port': 8000}
user = {'port': 9000, 'debug': True}
final = merge_configs(default, user)
print(final) # {'host': 'localhost', 'port': 9000, 'debug': True}
Repare que override vem depois de *configs, então só pode ser passado pelo nome: merge_configs(a, b, override=False).
5. Repassar argumentos para outra função
Um uso muito comum de *args, **kwargs é criar uma função “casca” que muda só um detalhe e repassa todo o resto para a função original, sem precisar repetir a lista de parâmetros:
1
2
3
4
5
6
7
8
9
def conectar(host, porta=5432, *, timeout=10, ssl=False):
return f"{host}:{porta} timeout={timeout} ssl={ssl}"
def conectar_producao(*args, **kwargs):
kwargs.setdefault("ssl", True) # padrão diferente só aqui
return conectar(*args, **kwargs)
print(conectar_producao("db.local"))
print(conectar_producao("db.local", 6543, timeout=3))
Saída:
1
2
db.local:5432 timeout=10 ssl=True
db.local:6543 timeout=3 ssl=True
Em decorators, use também functools.wraps, para que a função decorada não perca o nome e a docstring originais:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from functools import wraps
def log_chamada(func):
@wraps(func) # preserva nome e docstring da função original
def wrapper(*args, **kwargs):
print(f"Chamando {func.__name__} com args={args} kwargs={kwargs}")
return func(*args, **kwargs)
return wrapper
@log_chamada
def soma(a, b=0):
return a + b
print(soma(2, b=3))
print(soma.__name__)
Saída:
1
2
3
Chamando soma com args=(2,) kwargs={'b': 3}
5
soma
Sem o @wraps(func), a última linha mostraria wrapper em vez de soma.
Quer praticar *args, **kwargs e decorators construindo projetos guiados? Esse é um dos temas do curso de Python completo da Jornada Python.
Ordem de Parâmetros
1
2
3
4
5
6
7
8
9
10
11
12
13
def funcao(a, b, *args, key1, key2="default", **kwargs):
print(f"Posicionais obrigatórios: {a}, {b}")
print(f"Args extras: {args}")
print(f"Keyword obrigatório: {key1}")
print(f"Keyword com padrão: {key2}")
print(f"Kwargs extras: {kwargs}")
funcao(1, 2, 3, 4, key1="val1", key3="val3", key4="val4")
# Posicionais obrigatórios: 1, 2
# Args extras: (3, 4)
# Keyword obrigatório: val1
# Keyword com padrão: default
# Kwargs extras: {'key3': 'val3', 'key4': 'val4'}
Ordem SEMPRE:
- Posicionais obrigatórios:
a, b -
*args(variáveis posicionais) - Keywords obrigatórios:
key1 - Keywords com padrão:
key2="default" -
**kwargs(variáveis nomeados)
Dois detalhes que costumam cair em prova:
- Antes de
*args, parâmetros com valor padrão vêm depois dos obrigatórios:def f(a=1, b)geraSyntaxError: non-default argument follows default argument. - Depois de
*args, todos os parâmetros são keyword-only, então os itens 3 e 4 podem até se misturar:def f(*args, key2="d", key1)é válido.
A assinatura mais completa possível, com todos os tipos de parâmetro, fica assim:
1
2
3
4
5
def g(pos_only, /, normal, com_padrao=1, *args, kw_only, kw_padrao=2, **kwargs):
print(pos_only, normal, com_padrao, args, kw_only, kw_padrao, kwargs)
g(1, 2, 3, 4, 5, kw_only=6, extra=7)
# 1 2 3 (4, 5) 6 2 {'extra': 7}
E para escolher qual tipo de parâmetro usar em cada situação:
| Situação | Use | Exemplo |
|---|---|---|
| Valores fixos e obrigatórios | Parâmetro normal | def area(base, altura) |
| Valor opcional com padrão | Parâmetro com default | def saudar(nome, saudacao="Olá") |
| Quantidade variável de valores do mesmo tipo | *args |
def soma(*numeros) |
| Opções nomeadas livres (metadados, configurações) | **kwargs |
def log(msg, **meta) |
| Obrigar quem chama a escrever o nome | Keyword-only (depois de *) |
def conectar(host, *, timeout) |
| Impedir que chamem pelo nome | Só-posicional (antes de /) |
def distancia(x, y, /) |
| Repassar tudo para outra função | *args, **kwargs |
Wrappers e decorators |
Ir além do básico com parâmetros é ótimo, mas a base mesmo se constrói com prática guiada, de funções a POO. Conheça a Jornada Python:
Parâmetros só-posicionais (/) e só-nomeados (*)
Desde o Python 3.8 dá para controlar como cada parâmetro pode ser passado, usando dois marcadores na definição (veja parâmetros especiais na documentação oficial):
-
/: tudo que vem antes da barra é só-posicional (não pode ser passado pelo nome). -
*sozinho: tudo que vem depois do asterisco é só-nomeado, ou keyword-only (precisa ser passado comonome=valor).
1
2
3
4
5
def f(a, b, /, c, *, d):
print(a, b, c, d)
f(1, 2, 3, d=4) # 1 2 3 4
f(1, 2, c=3, d=4) # 1 2 3 4
Aqui, a e b são só-posicionais, c pode ser passado dos dois jeitos e d é só-nomeado. Quebrar essas regras gera TypeError:
1
2
3
4
5
f(a=1, b=2, c=3, d=4)
# TypeError: f() got some positional-only arguments passed as keyword arguments: 'a, b'
f(1, 2, 3, 4)
# TypeError: f() takes 3 positional arguments but 4 were given
O * sozinho é o *args sem nome: ele fecha a lista de posicionais, mas não aceita argumentos extras. Por isso ele precisa ser seguido de pelo menos um parâmetro nomeado (def f(*): pass gera SyntaxError: named arguments must follow bare *).
Para que serve na prática? Keyword-only deixa a chamada legível e evita trocar a ordem de opções parecidas:
1
2
3
4
5
def conectar(host, *, timeout=10, ssl=False):
print(f"Conectando em {host} (timeout={timeout}, ssl={ssl})")
conectar("db.local", timeout=5, ssl=True)
# Conectando em db.local (timeout=5, ssl=True)
As funções nativas usam isso o tempo todo. len() é só-posicional (len(obj=[1, 2]) dá TypeError: len() takes no keyword arguments), e em sorted() os parâmetros key e reverse são só-nomeados: sorted(palavras, key=len, reverse=True) funciona, sorted(palavras, None, True) dá TypeError: sorted expected 1 argument, got 3. É por isso que você sempre escreve key= ao passar uma função lambda para o sorted.
Erros comuns
1. SyntaxError por ordem errada dos parâmetros
Colocar **kwargs antes de *args (ou de qualquer outro parâmetro) não é permitido:
1
2
def f(**kwargs, *args):
pass
1
SyntaxError: arguments cannot follow var-keyword argument
Essa é a mensagem do Python 3.11 em diante; no 3.10 aparece só SyntaxError: invalid syntax. Na chamada, o equivalente é passar um argumento posicional depois de um nomeado: f(a=1, 2) dá SyntaxError: positional argument follows keyword argument.
Correção: siga a ordem posicionais, *args, keyword-only, **kwargs, tanto na definição (def f(*args, **kwargs)) quanto na chamada (f(2, a=1)).
2. TypeError: got multiple values for argument
Acontece quando o mesmo parâmetro recebe valor duas vezes: uma pela posição e outra pelo nome, muitas vezes escondido dentro de um **dicionario:
1
2
3
4
5
def saudar(nome, saudacao="Olá"):
print(f"{saudacao}, {nome}!")
dados = {"nome": "Ana"}
saudar("Bia", **dados)
1
TypeError: saudar() got multiple values for argument 'nome'
Correção: passe cada parâmetro uma vez só, saudar(**dados) ou saudar("Bia").
3. TypeError: got an unexpected keyword argument
Ao desempacotar um dicionário com **, toda chave precisa corresponder a um parâmetro da função:
1
2
3
4
5
def criar_usuario(nome, idade):
return {"nome": nome, "idade": idade}
dados = {"nome": "Ana", "idade": 30, "email": "[email protected]"}
criar_usuario(**dados)
1
TypeError: criar_usuario() got an unexpected keyword argument 'email'
Correção: adicione o parâmetro que falta, aceite extras com def criar_usuario(nome, idade, **kwargs) ou filtre o dicionário antes: criar_usuario(nome=dados["nome"], idade=dados["idade"]).
4. Confundir ** de potência com ** de desempacotamento
O ** entre números é potência, mas na chamada de função ele espera um dicionário. Usar ** com uma lista (em vez de *) quebra:
1
2
3
4
5
def potencia(base, expoente):
return base ** expoente # aqui ** é potência
valores = [2, 3]
print(potencia(**valores)) # aqui ** é desempacotamento de dict
1
TypeError: __main__.potencia() argument after ** must be a mapping, not list
Correção: lista ou tupla se desempacota com um asterisco, potencia(*valores) (resultado 8); dois asteriscos só com dicionário, potencia(**{"base": 2, "expoente": 3}).
5. Tratar args como lista
args é uma tupla, e tuplas são imutáveis:
1
2
3
4
def adicionar(*args):
args.append(4)
adicionar(1, 2, 3)
1
AttributeError: 'tuple' object has no attribute 'append'
Correção: converta antes de modificar, valores = list(args), e trabalhe com valores.
Exercícios resolvidos
Exercício 1. Qual é a saída do código abaixo?
1
2
3
4
def f(*args):
print(type(args), len(args))
f(10, 20, 30)
Ver solução
Saída: <class 'tuple'> 3
*args sempre entrega os argumentos posicionais extras como uma tupla, e foram passados 3 valores.
Exercício 2. Escreva uma função media(*numeros) que devolve a média de quantos números forem passados e devolve 0 se nenhum número for passado.
Ver solução
1
2
3
4
5
6
7
def media(*numeros):
if not numeros:
return 0
return sum(numeros) / len(numeros)
print(media(7, 8, 9)) # 8.0
print(media()) # 0
Saída: 8.0 e 0
Como numeros é uma tupla, sum() e len() funcionam direto. A checagem if not numeros evita ZeroDivisionError quando a tupla está vazia.
Exercício 3 (estilo prova). Considere a função def f(a, *args, **kwargs): print(a, args, kwargs). Qual é a saída de f(1, 2, 3, x=4)?
- a)
1 (2, 3) {'x': 4} - b)
1 [2, 3] {'x': 4} - c)
(1, 2, 3) {'x': 4} - d)
TypeError
Ver solução
1
2
3
4
def f(a, *args, **kwargs):
print(a, args, kwargs)
f(1, 2, 3, x=4)
Saída: 1 (2, 3) {'x': 4}
Resposta: a. O 1 preenche a, os posicionais que sobram vão para args como tupla (por isso não é a letra b) e o nomeado x=4 vai para kwargs.
Exercício 4. Escreva montar_url(base, **params) que devolve a URL com os parâmetros de busca. Exemplo: montar_url("https://site.com/busca", q="python", pagina=2) deve devolver https://site.com/busca?q=python&pagina=2. Sem parâmetros, devolve só a base.
Ver solução
1
2
3
4
5
6
7
8
def montar_url(base, **params):
if not params:
return base
query = "&".join(f"{chave}={valor}" for chave, valor in params.items())
return f"{base}?{query}"
print(montar_url("https://site.com/busca", q="python", pagina=2))
print(montar_url("https://site.com"))
Saída: https://site.com/busca?q=python&pagina=2 e https://site.com
params é um dicionário comum, então .items() entrega os pares nome/valor na ordem em que foram passados.
Exercício 5 (estilo prova). Qual das definições abaixo é válida em Python?
- a)
def f(**kwargs, *args): - b)
def f(*args, a, **kwargs): - c)
def f(a=1, b): - d)
def f(**kwargs, a):
Ver solução
Resposta: b. Depois de *args, o parâmetro a é keyword-only (precisa ser chamado como a=...) e **kwargs fecha a lista, como manda a regra.
As outras geram SyntaxError: em a e d há parâmetros depois de **kwargs, e em c um parâmetro sem padrão vem depois de um com padrão (non-default argument follows default argument).
Exercício 6. Chame a função distancia(x, y) abaixo usando desempacotamento: uma vez com a tupla ponto = (3, 4) e outra com o dicionário coordenadas = {"x": 6, "y": 8}.
Ver solução
1
2
3
4
5
6
7
8
def distancia(x, y):
return (x ** 2 + y ** 2) ** 0.5
ponto = (3, 4)
coordenadas = {"x": 6, "y": 8}
print(distancia(*ponto)) # 5.0
print(distancia(**coordenadas)) # 10.0
Saída: 5.0 e 10.0
Tupla usa um asterisco (os valores entram pela posição) e dicionário usa dois (entram pelo nome). Repare que dentro da função ** é potência: ** 0.5 é a raiz quadrada.
Exercício 7. Qual é a saída de cada print?
1
2
3
4
5
padrao = {"tema": "claro", "idioma": "pt"}
usuario = {"tema": "escuro"}
print({**padrao, **usuario})
print({**usuario, **padrao})
Ver solução
Saída:
1
2
{'tema': 'escuro', 'idioma': 'pt'}
{'tema': 'claro', 'idioma': 'pt'}
Ao juntar dicionários com **, em chave repetida vence o último dicionário. Por isso a ordem define se prevalece a escolha do usuário ou o padrão.
Exercício 8 (estilo prova). Dada a função def f(a, /, b, *, c): return a + b + c, qual chamada funciona?
- a)
f(a=1, b=2, c=3) - b)
f(1, 2, 3) - c)
f(1, b=2, c=3) - d)
f(1, 2, c=3, d=4)
Ver solução
1
2
3
4
def f(a, /, b, *, c):
return a + b + c
print(f(1, b=2, c=3))
Saída: 6
Resposta: c. As outras falham com TypeError: em a, a é só-posicional e não pode ir pelo nome; em b, c é só-nomeado (f() takes 2 positional arguments but 3 were given); em d, não existe parâmetro d (f() got an unexpected keyword argument 'd').
Conclusão
Neste guia de *args e **kwargs, você aprendeu:
✅ *args - Tupla de argumentos posicionais variáveis
✅ **kwargs - Dict de argumentos nomeados variáveis
✅ Desempacotamento - func(*lista), func(**dict) e {**a, **b}
✅ Ordem de parâmetros - Posicionais → *args → Keywords → **kwargs
✅ Casos práticos - Decorators, loggers, wrappers
Principais lições:
-
*argscaptura argumentos posicionais extras -
**kwargscaptura argumentos nomeados extras -
**entre números é potência; em funções e dicionários é desempacotamento - Essenciais para decorators genéricos
- Use para APIs flexíveis
- Respeite a ordem de parâmetros!
Próximos passos:
- Pratique Decorators
- Aprenda Funções avançadas
- Explore type hints com
*args: inte**kwargs: str - Refaça os exercícios de
*(keyword-only) e/(positional-only) sem olhar a solução
Agora você entende de onde vem esse tal de *args e **kwargs!
Se tiver qualquer dúvida, manda aqui embaixo no box de comentários ![]()
Até a próxima!
Perguntas frequentes
O que são *args e **kwargs em Python?
São parâmetros especiais que deixam uma função receber um número variável de argumentos. *args junta os argumentos posicionais extras numa tupla e **kwargs junta os argumentos nomeados extras num dicionário. Exemplo: em def f(*args, **kwargs), a chamada f(1, 2, nome='Ana') resulta em args = (1, 2) e kwargs = {'nome': 'Ana'}. Os nomes args e kwargs são só convenção: o que importa são o * e o **.
O que significa ** em Python?
Depende de onde o ** aparece. Entre dois números ele é o operador de potência: 2 ** 3 resulta em 8. Na definição de uma função (def f(**kwargs)) ele junta os argumentos nomeados extras num dicionário. Na chamada de uma função (f(**dados)) ele desempacota um dicionário em argumentos nomeados. Dentro de chaves ({**a, **b}) ele copia os pares de um dicionário para outro, o que serve para juntar dicionários.
Qual a diferença entre *args e **kwargs?
*args captura argumentos posicionais (passados sem nome, como f(1, 2)) e os entrega como uma tupla. **kwargs captura argumentos nomeados (passados como f(a=1, b=2)) e os entrega como um dicionário, com o nome do argumento como chave. Uma função pode ter os dois ao mesmo tempo, sempre com *args antes de **kwargs.
Qual a ordem correta dos parâmetros em uma função Python?
A ordem é: parâmetros posicionais (primeiro os obrigatórios, depois os com valor padrão), *args, parâmetros keyword-only e, por último, **kwargs. Exemplo válido: def f(a, b=2, *args, c, d=4, **kwargs). Colocar **kwargs antes de qualquer outro parâmetro gera SyntaxError. Se houver parâmetros só-posicionais, eles vêm no começo, antes de uma barra /.
Como passar uma lista ou um dicionário como argumentos de uma função?
Use o desempacotamento na chamada. Com * uma lista ou tupla vira argumentos posicionais: soma(*[1, 2]) é o mesmo que soma(1, 2). Com ** um dicionário vira argumentos nomeados: cadastrar(**{'nome': 'Ana', 'idade': 30}) é o mesmo que cadastrar(nome='Ana', idade=30). As chaves do dicionário precisam ter os mesmos nomes dos parâmetros, senão o Python lança TypeError: got an unexpected keyword argument.
Como juntar dois dicionários usando **?
Crie um dicionário novo desempacotando os dois: config = {**padrao, **usuario}. Se a mesma chave existir nos dois, vence o valor do último dicionário, então {**{'tema': 'claro'}, **{'tema': 'escuro'}} resulta em {'tema': 'escuro'}. Os originais não são alterados. A partir do Python 3.9 também existe o operador de união de dicionários (a barra vertical), com o mesmo resultado.
Ao definir def f(a, *args, **kwargs) e chamar f(1, 2, 3, x=4), quais são os valores de a, args e kwargs?
a recebe 1, args recebe a tupla (2, 3) e kwargs recebe o dicionário {'x': 4}. O primeiro argumento posicional preenche o parâmetro a, os posicionais que sobram vão para *args (sempre como tupla, não lista) e os argumentos nomeados sem parâmetro correspondente vão para **kwargs.
O que significam / e * sozinhos na lista de parâmetros de uma função?
A barra / marca que os parâmetros antes dela são só-posicionais: não podem ser passados pelo nome. O asterisco * sozinho marca que os parâmetros depois dele são só-nomeados (keyword-only): precisam ser passados como nome=valor. Em def f(a, /, b, *, c), a chamada f(1, 2, c=3) funciona, f(a=1, b=2, c=3) falha porque a é só-posicional, e f(1, 2, 3) falha porque c é só-nomeado.
"Porque o Senhor dá a sabedoria, e da sua boca vem a inteligência e o entendimento" Pv 2:6