*args e **kwargs no Python: o que são e como usar

Cansado de programar?

Conheça a melhor e mais completa formação de Python e Django e sinta-se um programador verdadeiramente competente. Além de Python e Django, você também vai aprender Banco de Dados, SQL, HTML, CSS, Javascript, Bootstrap e muito mais!

Quero aprender Python e Django de Verdade! Quero aprender!
Suporte

Tire suas dúvidas diretamente com o professor

Projetos práticos

Projetos práticos voltados para o mercado de trabalho

Prática profissional

Formação moderna com foco na prática profissional

Download do Artigo

✅ 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 args e kwargs são convenção: o que importa é o * e o **.
  • Ordem na definição: posicionais, *args, keyword-only, **kwargs.
  • args é uma tupla (imutável) e kwargs é um dict.
  • Entre dois números, ** é potência (2 ** 3 dá 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! :wink:

Vá Direto ao Assunto…

*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 :laughing:

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

:bulb: 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? :thumbsup:

Que tal receber 30 dias de conteúdo direto na sua Caixa de Entrada?

Sua assinatura não pôde ser validada.
Você fez sua assinatura com sucesso.

Assine as PyDicas e receba 30 dias do melhor conteúdo Python na sua Caixa de Entrada: direto e sem enrolação!

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 gera TypeError: ... 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:

  1. Posicionais obrigatórios: a, b
  2. *args (variáveis posicionais)
  3. Keywords obrigatórios: key1
  4. Keywords com padrão: key2="default"
  5. **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) gera SyntaxError: 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 como nome=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:

  • *args captura argumentos posicionais extras
  • **kwargs captura 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: int e **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 :wink:

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.

Começe agora sua Jornada na Programação!

Não deixe para amanhã o sucesso que você pode começar a construir hoje!

#newsletter Olá :wave: Curtiu o artigo? Então faça parte da nossa Newsletter! Privacidade Não se preocupe, respeitamos sua privacidade. Você pode se descadastrar a qualquer momento.