Argumentos de funções
Nesta aula, exploramos em profundidade os diferentes tipos de argumentos de funções em Python: posicionais, nomeados, valores padrão, *args, **kwargs e argumentos keyword-only. Você aprenderá como cada um funciona, quando utilizá-los e boas práticas para escrever funções flexíveis e claras.
Funções são blocos fundamentais em Python, e a forma como passamos argumentos para elas é crucial para escrever código flexível e legível. Nesta aula, vamos explorar os diferentes mecanismos de passagem de argumentos: argumentos posicionais, nomeados, valores padrão, coleta de argumentos variáveis com *args e **kwargs, e argumentos keyword-only. Ao final, você será capaz de projetar funções que se adaptam a diferentes necessidades sem sacrificar a clareza.
Dominar esses conceitos é essencial para trabalhar com bibliotecas, criar APIs elegantes e evitar erros comuns. Vamos começar!
Posicionais e nomeados
Em Python, ao chamar uma função, podemos passar argumentos de duas formas: posicionalmente (na ordem dos parâmetros) ou por nome (keyword). Argumentos posicionais são mapeados aos parâmetros pela ordem em que são fornecidos. Já os argumentos nomeados são associados explicitamente a um parâmetro, independentemente da ordem.
Exemplo:
def saudacao(nome, mensagem):
print(f"{mensagem}, {nome}!")
# Chamada posicional
saudacao("Alice", "Olá") # Saída: Olá, Alice!
# Chamada nomeada
saudacao(mensagem="Oi", nome="Bob") # Saída: Oi, Bob!
É possível misturar argumentos posicionais e nomeados, mas todos os argumentos posicionais devem vir antes dos nomeados. Caso contrário, ocorrerá um erro de sintaxe.
Valores padrão
Parâmetros podem ter valores padrão, tornando-os opcionais na chamada. Se o argumento não for fornecido, o valor padrão é usado. É importante lembrar que os valores padrão são avaliados apenas uma vez, no momento da definição da função, e não a cada chamada.
Exemplo:
def saudacao(nome, mensagem="Olá"):
print(f"{mensagem}, {nome}!")
saudacao("Alice") # Olá, Alice!
saudacao("Bob", "Oi") # Oi, Bob!
Cuidado com valores mutáveis como padrão, pois eles são compartilhados entre chamadas:
def adiciona_item(item, lista=[]):
lista.append(item)
return lista
print(adiciona_item(1)) # [1]
print(adiciona_item(2)) # [1, 2] (compartilha a mesma lista!)
Para evitar isso, use None e crie um novo objeto dentro da função:
def adiciona_item(item, lista=None):
if lista is None:
lista = []
lista.append(item)
return lista
*args e **kwargs
Às vezes, queremos que uma função aceite um número variável de argumentos. O operador *args coleta argumentos posicionais extras em uma tupla, enquanto **kwargs coleta argumentos nomeados extras em um dicionário. Os nomes args e kwargs são convenções, mas os asteriscos são obrigatórios.
Exemplo:
def func(a, b, *args, **kwargs):
print(f"a = {a}, b = {b}")
print(f"args = {args}")
print(f"kwargs = {kwargs}")
func(1, 2, 3, 4, x=5, y=6)
# Saída:
# a = 1, b = 2
# args = (3, 4)
# kwargs = {'x': 5, 'y': 6}
Essa técnica é muito usada em wrappers, decoradores e funções que precisam repassar argumentos para outras funções.
Argumentos keyword-only
Argumentos keyword-only são aqueles que só podem ser passados por nome, nunca posicionalmente. Eles vêm depois de * (ou depois de *args). Isso força o chamador a ser explícito, melhorando a legibilidade.
Exemplo:
def func(a, b, *, opcao1, opcao2):
print(f"a={a}, b={b}, opcao1={opcao1}, opcao2={opcao2}")
func(1, 2, opcao1="sim", opcao2="não") # Válido
func(1, 2, "sim", "não") # Erro! TypeError: func() takes 2 positional arguments but 4 were given
Também é possível ter argumentos keyword-only com valores padrão, tornando-os opcionais:
def func(a, b, *, opcao1="padrão", opcao2=None):
pass
Isso é útil para parâmetros de configuração ou flags que tornam a chamada mais clara.
Boas práticas e observações finais
Use argumentos posicionais para parâmetros obrigatórios e óbvios pela ordem. Prefira argumentos nomeados para parâmetros opcionais ou quando muitos parâmetros podem tornar a ordem confusa. Valores padrão devem ser imutáveis para evitar comportamentos inesperados. *args e **kwargs são poderosos, mas use com moderação para não esconder a assinatura da função. Argumentos keyword-only são ótimos para forçar clareza em APIs públicas.
Referências
- Documentação oficial: Definindo funções
- Mais sobre definição de funções
- FAQ: Como passar parâmetros opcionais ou de palavra-chave
- PEP 3102 – Keyword-Only Arguments
- Real Python: *args e **kwargs
Exercícios
- Exercício 1: Defina uma função
cumprimentarque aceita um argumento posicionalnomee um argumento nomeadosaudacaocom valor padrão "Olá". A função deve imprimir a saudação. Teste com e sem o argumento nomeado. - Exercício 2: Crie uma função
somaque aceita um número variável de argumentos posicionais (usando*args) e retorna a soma de todos eles. Teste com diferentes quantidades de números. - Exercício 3: Escreva uma função
monta_dicionarioque aceita argumentos nomeados variáveis (**kwargs) e retorna um dicionário com esses pares chave-valor. Teste com alguns argumentos. - Exercício 4: Defina uma função
configurarque recebe dois parâmetros posicionais (hosteporta) e dois parâmetros keyword-only (ssletimeout) com valores padrãoTruee30, respectivamente. A função deve imprimir todos os parâmetros. Chame a função de duas maneiras: uma passando os keyword-only e outra omitindo. - Exercício 5: Crie uma função
calculadoraque aceita um operador (string) como primeiro argumento posicional, seguido de*argspara números. Dependendo do operador ('+', '-', '*', '/'), realize a operação correspondente sobre todos os números (pelo menos dois). Usesumpara soma,reducepara subtração etc. (importefunctools.reduce). Teste com diferentes operadores.
def cumprimentar(nome, saudacao="Olá"):
print(f"{saudacao}, {nome}!")
cumprimentar("Ana")
cumprimentar("Pedro", saudacao="Oi")
def soma(*args):
return sum(args)
print(soma(1, 2, 3))
print(soma(10, 20))
print(soma())
def monta_dicionario(**kwargs):
return kwargs
print(monta_dicionario(nome="Alice", idade=30, cidade="São Paulo"))
def configurar(host, porta, *, ssl=True, timeout=30):
print(f"host={host}, porta={porta}, ssl={ssl}, timeout={timeout}")
configurar("localhost", 8080, ssl=False, timeout=60)
configurar("127.0.0.1", 8000)
from functools import reduce
def calculadora(operador, *args):
if operador == '+':
return sum(args)
elif operador == '-':
return reduce(lambda x, y: x - y, args)
elif operador == '*':
return reduce(lambda x, y: x * y, args)
elif operador == '/':
return reduce(lambda x, y: x / y, args)
else:
raise ValueError("Operador inválido")
print(calculadora('+', 1, 2, 3))
print(calculadora('-', 10, 2, 3))
print(calculadora('*', 2, 3, 4))
print(calculadora('/', 100, 2, 5))