Python *args e **kwargs explicados: funções flexíveis com exemplos claros

  1. Início
  2. Blog
  3. Desenvolvimento de software

Desenvolvimento de Software

Python *args e **kwargs explicados: funções flexíveis com exemplos claros

Essas estrelas nas assinaturas de funções são menos misteriosas do que parecem. Aprenda como *args e **kwargs coletam argumentos, como as mesmas estrelas os descompactam e como projetar assinaturas de função que são difíceis de usar indevidamente.

8 min de leitura
Python *args e **kwargs explicados: funções flexíveis com exemplos claros

Principais pontos

  • *args coleta argumentos posicionais extras em uma tupla.
  • **kwargs coleta argumentos extras de palavras-chave em um dicionário.
  • Os nomes são apenas uma convenção, pois * e ** são o que importa.
  • As mesmas estrelas descompactam uma lista ou dicionário quando você chama uma função.

Mais cedo ou mais tarde, você lerá uma função como def handler(*args, **kwargs) e se perguntará o que ela aceita. Python *args e **kwargs permitem que uma função receba um número flexível de argumentos. Eles potencializam decoradores, wrappers e muitas APIs de biblioteca.

Este guia aborda como eles funcionam em ambas as direções, juntamente com as novas maneiras pelas quais o Python permite controlar como uma função é chamada. Executamos cada exemplo no Python 3.13 e a saída mostrada vem dessa execução.

O que *args faz

Um parâmetro escrito com um asterisco coleta quaisquer argumentos posicionais extras em uma tupla:

def total(*args):
    print(type(args).__name__, args)
    return sum(args)

print(total(1, 2, 3))
# tuple (1, 2, 3)
# 6

print(total())
# tuple ()
# 0

Sem argumentos, você simplesmente obtém uma tupla vazia, então a função ainda funciona. O nome args é uma convenção. *numbers se comportaria da mesma forma, e um nome mais descritivo geralmente é melhor.

O que **kwargs faz

Duas estrelas coletam quaisquer argumentos extras de palavras-chave em um dicionário:

def describe(**kwargs):
    print(type(kwargs).__name__, kwargs)

describe(name="Asha", city="Pune")
# dict {'name': 'Asha', 'city': 'Pune'}

Por ser um dicionário normal, você pode fazer um loop nele, procurar chaves com get() e passá-lo adiante. As chaves mantêm a ordem em que foram passadas.

Ordem dos parâmetros

Quando você mistura tipos de parâmetros, o Python requer uma ordem específica:

  1. Parâmetros posicionais regulares
  2. *argumentos
  3. Parâmetros somente de palavras-chave
  4. **kwargs
def order(item, qty=1, *extras, priority=False, **notes):
    print(item, qty, extras, priority, notes)

order("pen")
# pen 1 () False {}

order("pen", 2, "gift wrap", "card", priority=True, note="leave at door")
# pen 2 ('gift wrap', 'card') True {'note': 'leave at door'}

Observe priority. Como vem depois de *extras, ele só pode ser definido pelo nome, portanto, um argumento posicional perdido nunca poderia defini-lo por acidente. Colocar **kwargs antes não é permitido, e nosso teste gerou um SyntaxError: ​​”os argumentos não podem seguir o argumento var-keyword”.

Desempacotando ao ligar

As mesmas estrelas funcionam ao contrário no local da chamada. Uma estrela espalha uma lista ou tupla em argumentos posicionais e duas estrelas espalham um dicionário em argumentos de palavras-chave:

nums = [4, 5, 6]
total(*nums)            # same as total(4, 5, 6) -> 15

settings = {"name": "Ben", "city": "Delhi"}
describe(**settings)    # same as describe(name="Ben", city="Delhi")

A descompactação funciona com qualquer função, não apenas aquelas que usam *args e **kwargs. Isso é útil quando a configuração chega como um dicionário:

def connect(host, port, timeout=5):
    return f"{host}:{port} (timeout {timeout})"

connect(**{"host": "db.local", "port": 5432})
# 'db.local:5432 (timeout 5)'

connect(**{"host": "db.local", "port": 5432, "ssl": True})
# TypeError: connect() got an unexpected keyword argument 'ssl'

Esse erro é útil. Uma função sem **kwargs rejeita opções desconhecidas, portanto, um erro de digitação no nome de uma configuração falha ruidosamente em vez de ser ignorado silenciosamente.

Argumentos somente de palavras-chave

Um simples * na assinatura, sem nome, significa “tudo depois disso deve ser passado por palavra-chave”:

def make_user(name, *, admin=False):
    return name, admin

make_user("Asha", admin=True)
# ('Asha', True)

make_user("Asha", True)
# TypeError: make_user() takes 1 positional argument but 2 were given

Esta é uma ótima maneira de proteger sinalizadores booleanos. Uma chamada como make_user("Asha", True) força os leitores a adivinhar o que True significa. Exigir admin=True torna cada chamada autoexplicativa.

Argumentos apenas posicionais

Desde o Python 3.8, um / marca parâmetros que só podem ser passados por posição:

def area(width, height, /):
    return width * height

area(3, 4)                 # 12
area(width=3, height=4)
# TypeError: area() got some positional-only arguments
#   passed as keyword arguments: 'width, height'

Isso permite que uma biblioteca renomeie esses parâmetros posteriormente sem quebrar o código de ninguém, já que ninguém pode depender dos nomes. Vários integrados funcionam dessa maneira. Por exemplo, len(obj=[1]) não funciona.

Dica: O padrão completo é def f(pos_only, /, normal, *, kw_only). Você raramente precisa dos três, mas saber o que / e * significam ajuda a ler a documentação e as assinaturas da biblioteca.

Decoradores e embalagens

Esta é a principal razão do mundo real para usar os dois juntos. Um wrapper geralmente precisa aceitar quaisquer argumentos que a função original receba e transmiti-los inalterados:

import functools, time

def timed(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        print(f"{func.__name__} took {time.perf_counter() - start:.4f}s")
        return result
    return wrapper

@timed
def slow_add(a, b, delay=0.01):
    time.sleep(delay)
    return a + b

print(slow_add(2, 3, delay=0.02))
# slow_add took 0.0205s
# 5

O wrapper não precisa conhecer os parâmetros de slow_add, portanto funciona para qualquer função. Seu valor de tempo será um pouco diferente. Nosso guia para decoradores Python explica functools.wraps e outros detalhes.

Mesclando opções

** também funciona dentro de um literal de dicionário, o que oferece uma maneira elegante de aplicar substituições sobre os padrões:

defaults = {"theme": "light", "lang": "en"}

def build(**overrides):
    return {**defaults, **overrides}

build(theme="dark")
# {'theme': 'dark', 'lang': 'en'}

As chaves posteriores vencem, portanto a substituição substitui o padrão. As subclasses também usam esse padrão, como em super().__init__(*args, **kwargs), para passar argumentos extras para uma classe pai. Consulte Classes e objetos Python para obter mais informações sobre super().

Folha de dicas

Sintaxe Onde Significado
def f(*argumentos) Definição Colete argumentos posicionais extras em uma tupla
def f(**kwargs) Definição Colete argumentos extras de palavras-chave em um ditado
def f(uma, *, b) Definição b é apenas palavra-chave
def f(a, /, b) Definição a é apenas posicional
f(*items) Ligar Espalhe uma sequência em argumentos posicionais
f(**options) Ligar Espalhe um ditado em argumentos de palavras-chave
{**a, **b} Literalmente Mesclar dicionários
[*a, *b] Literalmente Combine iteráveis em uma lista

Quando evitá-los

A flexibilidade tem um preço. Uma função definida como def create(**kwargs) não informa aos leitores quais opções existem e os editores não podem completá-las automaticamente. Os erros de digitação podem passar silenciosamente por várias camadas antes que algo falhe.

Portanto, prefira parâmetros explícitos quando souber o que uma função precisa. Use *args e **kwargs quando você realmente precisar de flexibilidade: wrappers, decoradores, encaminhamento de argumentos para outra função ou uma entrada verdadeira de comprimento variável como total(*numbers). Se uma função precisar de muitas opções nomeadas, um dataclass também poderá ser mais claro.

Erros comuns

Usando um padrão mutável

Não se trata de estrelas, mas muitas vezes aparece perto delas. Um valor padrão é criado uma vez, quando a função é definida, e então compartilhado entre chamadas:

def bad(items=[]):
    items.append(1)
    return items

print(bad(), bad())
# [1, 1] [1, 1]

Ambas as chamadas retornaram a mesma lista, que até então continha dois itens. Use items=None e crie uma nova lista dentro da função.

Outros erros

  • Esquecendo de desempacotar. Ligar para total(nums) passa uma lista em vez de três números. Escreva total(*nums).
  • Engolindo erros de digitação. Uma função que aceita **kwargs, mas ignora chaves desconhecidas, oculta opções com erros ortográficos. Valide-os ou evite **kwargs.
  • Passar o mesmo argumento duas vezes. f(1, **{"a": 2}), onde a é o primeiro parâmetro, gera um erro de “valores múltiplos”.
  • Não encaminhando em um wrapper. Chamar func(args, kwargs) passa uma tupla e um ditado. A forma correta é func(*args, **kwargs).

Digite dicas para *args e **kwargs

As dicas de tipo em *args e **kwargs descrevem cada valor individual, não a coleção inteira. Isso surpreende muitas pessoas:

def total(*prices: float) -> float:
    return sum(prices)      # prices is tuple[float, ...]

def tag(**attrs: str) -> str:
    return " ".join(f'{k}="{v}"' for k, v in attrs.items())   # dict[str, str]

total(1.5, 2.5)                  # 4.0
tag(id="x", role="button")       # id="x" role="button"

Aqui prices é tratado como uma tupla de pontos flutuantes e attrs como um dicionário de strings. Quando os argumentos de palavras-chave têm tipos diferentes, o Python 3.12 adicionou uma opção precisa: um TypedDict combinado com Unpack.

from typing import TypedDict, Unpack

class Options(TypedDict, total=False):
    timeout: float
    retries: int

def fetch(url: str, **opts: Unpack[Options]) -> None: ...

Um verificador de tipo como mypy ou Pyright pode então rejeitar fetch(url, retries="3") ou um nome de opção com erro ortográfico, o que um simples **kwargs nunca pode fazer. O comportamento do tempo de execução não muda, no entanto. Quando executamos fetch("u", retries="3") no Python 3.13, ele foi executado sem reclamações, porque o próprio Python não impõe dicas de tipo.

Se você estiver documentando uma longa lista de chaves aceitas para **kwargs, considere substituí-las por parâmetros explícitos apenas de palavras-chave. Os editores podem então preenchê-los automaticamente e os erros são detectados imediatamente.

Considerações finais

Lembre-se das duas direções. Numa definição, as estrelas se acumulam. Em uma ligação, eles desfazem as malas. Adicione argumentos somente de palavras-chave para tornar as chamadas mais claras e reserve **kwargs para código que encaminhe argumentos genuinamente. Para praticar, escreva um pequeno decorador que registre os argumentos de cada chamada e use-o em algumas funções diferentes.

Perguntas frequentes

Qual é a diferença entre *args e **kwargs?

*args coleta argumentos posicionais extras em uma tupla. **kwargs coleta argumentos extras de palavras-chave em um dicionário. Uma função pode usar os dois juntos.

Devo chamá-los de args e kwargs?

Não. As estrelas fazem o trabalho, então *valores ou **opções são igualmente válidos. args e kwargs são simplesmente uma convenção amplamente reconhecida.

Em que ordem *args e **kwargs devem estar?

Parâmetros regulares primeiro, depois *args, depois parâmetros somente de palavras-chave e **kwargs por último. Qualquer outra ordem causa um SyntaxError.

O que significa um único * em uma definição de função?

Um * vazio significa que todos os parâmetros depois devem ser passados ​​por palavra-chave. Torna as chamadas mais claras, especialmente para sinalizadores booleanos.

O que / significa em uma função Python?

Os parâmetros antes de / são apenas posicionais, portanto, os chamadores não podem passá-los pelo nome. Este recurso foi adicionado no Python 3.8.

Continue aprendendo

Trabalhe com Stack Quora

Precisa de ajuda com testes, WordPress ou SEO?

Ajudamos as equipes a fornecer software melhor e a expandir seus sites. Conte-nos no que você está trabalhando e sugeriremos um próximo passo claro.

Continue lendo

Mais guias sobre testes, IA e desenvolvimento moderno

Gostou deste artigo? Escolha um tópico e vá direto para nossos tutoriais mais recentes.

Inteligência artificial

Explicação das alucinações de IA: por que os modelos de linguagem inventam as coisas e como reduzi-las

Explicação das alucinações de IA: o que são, por que os modelos de linguagem as produzem, tipos comuns, como reduzi-las com aterramento e instruções…

Leia o artigo →
DevOps

Variáveis de ambiente explicadas: arquivos .env, segredos e configuração bem feitos

Variáveis de ambiente explicadas com exemplos de Python que executamos: leitura e configuração, por que cada valor é uma string, arquivos .env, hera…

Leia o artigo →
Desenvolvimento de software

Explicação da limitação de taxa de API: algoritmos, cabeçalhos e tratamento de erros 429

Limitação de taxa de API explicada com uma simulação que executamos: token bucket, janela fixa, janela deslizante, 429 respostas, cabeçalhos de limi…

Leia o artigo →
Automação de testes

Zombando em Python: unittest.mock, patch e os erros que fazem os testes mentirem

Zombaria em Python com unittest.mock, testado: Mock e return_value, side_effect, patch onde um nome é usado, especificação e autospec, zombaria de d…

Leia o artigo →
Teste de software

Cobertura de código explicada: cobertura de linha versus filial e o que 100% realmente significa

Cobertura de código explicada com uma execução cobertura.py real: cobertura de linha, ramal e condição, leitura de um relatório, por que 100% de cob…

Leia o artigo →
Bancos de dados e SQL

DELETE vs TRUNCATE vs DROP em SQL: diferenças, reversão e quando usar cada um

DELETE vs TRUNCATE vs DROP explicado com SQL testado: o que cada um remove, rollback, aciona, redefinição de identidade, velocidade e como PostgreSQ…

Leia o artigo →
Bancos de dados e SQL

Exibições SQL explicadas: crie, use e evite as armadilhas, com exemplos

Visualizações SQL explicadas com consultas que executamos: criação de uma visualização, resultados ao vivo, ocultação de colunas, desempenho, visual…

Leia o artigo →
Desenvolvimento de software

Interface TypeScript vs tipo: diferenças reais, testadas com o compilador

Interface TypeScript versus tipo, verificada com o compilador real: o que ambos podem fazer, o que apenas um pode fazer, mesclagem de declarações, e…

Leia o artigo →
Desenvolvimento de software

Python *args e **kwargs explicados: funções flexíveis com exemplos claros

Python *args e **kwargs explicados com o código que executamos: empacotamento e descompactação, ordem dos parâmetros, argumentos somente de palavras…

Leia o artigo →
Desenvolvimento de software

Funções Python Lambda: sintaxe, usos reais e quando não usá-los

Funções lambda do Python explicadas com o código que executamos: sintaxe, classificação com chave, mapa e filtro, fechamentos, armadilha de ligação …

Leia o artigo →
Inteligência artificial

O que são agentes de IA? Como eles funcionam, com um loop de agente simples que você pode executar

O que são agentes de IA? Um guia simples para o loop do agente, ferramentas, memória e proteções, com um pequeno loop Python que executamos, riscos …

Leia o artigo →
DevOps

Monitoramento versus observabilidade: a verdadeira diferença, com logs, métricas e rastreamentos

Explicação do monitoramento versus observabilidade: falhas conhecidas e desconhecidas, logs, métricas e rastreamentos, um passo a passo do incidente…

Leia o artigo →
DevOps

Docker Volumes vs Bind Mounts: persista os dados do contêiner da maneira certa

Volumes Docker versus montagens de ligação explicadas: por que os dados do contêiner desaparecem, volumes nomeados, montagens de ligação, tmpfs, exe…

Leia o artigo →
Automação de testes

TDD vs BDD: principais diferenças, um recurso criado de ambas as maneiras e quando usar cada um

TDD vs BDD explicado com um recurso de desconto construído em ambos os sentidos: refatoração vermelho-verde com o código que executamos, cenários da…

Leia o artigo →
Desenvolvimento de software

Git merge vs rebase: diferenças, exemplos e quando usar cada um

Git merge vs rebase explicado com saída real do log, uma tabela comparativa, a regra de ouro do rebase e um fluxo de trabalho seguro para equipes.

Leia o artigo →
Desenvolvimento de software

List comprehension em Python: sintaxe, exemplos e quando evitar

List comprehension em Python explicada: sintaxe, filtros, if-else, laços aninhados, tempos medidos e quando um laço for comum é a melhor escolha.

Leia o artigo →
Bancos de dados e SQL

SQL JOIN explicado com exemplos (Inner, Left, Right, Full)

Entenda os joins SQL passo a passo com tabelas e consultas de exemplo: INNER, LEFT, RIGHT, FULL, CROSS e SELF, além de erros comuns e dicas de desem…

Leia o artigo →