Funções em Python: def, return, argumentos, *args e **kwargs, lambda e anotações de tipo
Função em Python é um bloco de código com nome, declarado pela palavra def, chamado pelo nome com parênteses e que devolve o resultado por meio de return; sem return a função devolve None. Os argumentos são passados por posição ou pelo nome, *args reúne os valores posicionais extras em uma tupla, **kwargs — os nomeados em um dicionário, e lambda cria uma função anônima curta com uma única expressão.
Uma função em Python é um trecho de código com nome, que pode ser chamado quantas vezes for preciso com dados diferentes. Ela é declarada com a palavra def, recebe valores por meio de parâmetros e devolve o resultado com return. As funções são a principal forma de transformar um script longo em um conjunto de etapas claras e testáveis.
Como declarar uma função: def python
A declaração de uma função começa com def, depois vêm o nome, os parâmetros entre parênteses e os dois-pontos. O corpo é escrito com recuo de quatro espaços — o recuo em Python não é enfeite, é parte da sintaxe.
def greet(name):
"""Devolve uma saudação para o usuário."""
return f"Olá, {name}!"
print(greet("Ana")) # Olá, Ana!
A string entre aspas triplas logo após o cabeçalho é a docstring, a documentação da função. Ela é exibida por help(greet) e pelas dicas do editor. Os nomes de funções seguem por convenção a PEP 8 e são escritos em snake_case: add_expense, get_rate.
A linha def greet(name): é chamada de assinatura da função: nome mais lista de parâmetros (e anotações, se houver). Pela assinatura, quem lê entende como chamar a função sem olhar o corpo.
O que faz o return em Python
return encerra a função e entrega um valor a quem a chamou. O código depois de um return já executado não roda. Se não houver return, ou se ele vier sem valor, a função devolve None.
def area(w, h):
w * h # resultado calculado e descartado
print(area(3, 4)) # None
def area(w, h):
return w * h
print(area(3, 4)) # 12
Um return esquecido é um dos erros mais comuns de quem está começando: a função «funciona», mas devolve None, e o problema aparece uma linha depois. Dá para devolver vários valores — na prática isso é uma tupla só, que pode ser desempacotada:
def min_max(numbers):
return min(numbers), max(numbers)
low, high = min_max([4, 1, 9])
print(low, high) # 1 9
O return antecipado ajuda a evitar aninhamento excessivo: primeiro tratamos os casos especiais, depois escrevemos a lógica principal.
def discount(price, is_member):
if price <= 0:
return 0
if not is_member:
return price
return price * 0.9
Argumentos de função em Python: posicionais e nomeados
Parâmetros são os nomes na declaração da função; argumentos são os valores passados na chamada. Os argumentos podem ser passados em ordem (posicionalmente) ou pelo nome (nomeadamente). Argumentos nomeados tornam a chamada autoexplicativa, e a ordem deixa de importar.
def add_expense(title, amount, category="outros"):
return {"title": title, "amount": amount, "category": category}
add_expense("Café", 250) # posicional
add_expense(amount=250, title="Café") # pelo nome
add_expense("Táxi", 600, category="transporte") # misto
O parâmetro com valor padrão é opcional. As regras: parâmetros com valor padrão vêm depois dos obrigatórios, e na chamada os argumentos posicionais vêm antes dos nomeados. Caso contrário, o Python exibe SyntaxError.
Armadilha: valor padrão mutável
O valor padrão é calculado uma única vez — no momento em que a linha def é executada, e não a cada chamada. Se for uma lista ou um dicionário, ele será compartilhado por todas as chamadas.
def add_item(item, acc=[]):
acc.append(item)
return acc
print(add_item(1)) # [1]
print(add_item(2)) # [1, 2] — a lista «lembra» a chamada anterior
A forma correta é usar None como padrão e criar um objeto novo dentro da função:
def add_item(item, acc=None):
if acc is None:
acc = []
acc.append(item)
return acc
*args e **kwargs: qualquer quantidade de argumentos
O asterisco antes do parâmetro reúne todos os argumentos posicionais extras em uma tupla (tuple), e dois asteriscos reúnem os nomeados extras em um dicionário (dict). Os nomes args e kwargs são apenas convenção — o que importa são os asteriscos.
def total(*args):
return sum(args)
print(total(100, 250, 40)) # 390
def make_user(name, **kwargs):
user = {"name": name}
user.update(kwargs)
return user
print(make_user("Ana", city="Recife", age=30))
# {'name': 'Ana', 'city': 'Recife', 'age': 30}
Os mesmos asteriscos funcionam no sentido inverso: na chamada eles desempacotam uma sequência ou um dicionário em argumentos:
values = [3, 4]
print(pow(*values)) # 81
opts = {"sep": " | ", "end": "\n"}
print("a", "b", **opts) # a | b
Parâmetros somente posicionais e somente nomeados
A barra / e o asterisco * na lista de parâmetros funcionam como barreiras: tudo antes de / é passado apenas por posição (sintaxe disponível desde o Python 3.8, PEP 570), tudo depois de * — apenas pelo nome.
def convert(amount, /, *, rate, currency="USD"):
return round(amount / rate, 2), currency
convert(100, rate=90.5) # (1.1, 'USD')
convert(amount=100, rate=90.5) # TypeError: amount só pode ser passado por posição
convert(100, 90.5) # TypeError: rate só pode ser passado pelo nome
Parâmetros apenas nomeados protegem contra confusões em chamadas como convert(100, 90.5, "EUR"), em que não fica claro o que é o quê.
Escopo de variáveis: a regra LEGB
O Python procura um nome de variável em uma cadeia: Local (dentro da função) → Enclosing (na função externa) → Global (no nível do módulo) → Built-in (nomes embutidos, como len). Ler uma variável global dentro de uma função é permitido, mas para atribuir a ela um novo valor é preciso a declaração global.
limit = 1000
def check(amount):
return amount > limit # ler a global — pode
def raise_limit():
global limit
limit = 2000 # sem global seria criada uma variável local
Na prática, global é melhor evitar: uma função que altera estado externo é difícil de testar. É mais seguro receber o valor como argumento e devolver o resultado.
Funções aninhadas e closures
Uma função pode ser declarada dentro de outra. A função interna enxerga as variáveis da externa mesmo depois de a externa ter terminado — isso é um closure (fechamento). Para alterar essa variável usa-se nonlocal.
def make_counter():
count = 0
def counter():
nonlocal count
count += 1
return count
return counter
c = make_counter()
print(c(), c(), c()) # 1 2 3
Closures estão na base dos decoradores e das fábricas de funções.
Função também é objeto
Em Python a função é um objeto comum: pode ser guardada em uma variável, passada a outra função, devolvida por uma função e armazenada em um dicionário. Ela tem atributos: __name__, __doc__, __defaults__, __annotations__.
def double(x):
return x * 2
f = double
print(f(5), f.__name__) # 10 double
print(list(map(double, [1, 2, 3]))) # [2, 4, 6]
Lambda em Python: funções anônimas
A função lambda é uma função curta sem nome, cujo corpo é uma única expressão. O resultado da expressão é devolvido automaticamente; escrever return não é permitido.
square = lambda x: x ** 2
print(square(4)) # 16
expenses = [("Café", 250), ("Táxi", 600), ("Almoço", 450)]
expenses.sort(key=lambda e: e[1], reverse=True)
print(expenses) # [('Táxi', 600), ('Almoço', 450), ('Café', 250)]
print(list(filter(lambda n: n % 2 == 0, range(10)))) # [0, 2, 4, 6, 8]
Lambdas em Python são adequadas como argumento curto para sorted, max, map, filter. Atribuir lambda a uma variável, como square acima, a PEP 8 não recomenda: se a função precisa de nome, escreva def — assim ela terá um __name__ normal no traceback e lugar para a docstring.
| def | lambda | |
|---|---|---|
| Nome | tem | não tem (<lambda>) |
| Corpo | qualquer número de instruções | uma expressão |
| return | explícito | implícito |
| Docstring, anotações | sim | não |
| Onde cabe | em qualquer lugar | key curto ou callback |
Anotações de tipo: dicas, não verificações
As anotações descrevem os tipos esperados dos parâmetros e do resultado. O próprio Python não as verifica durante a execução: uma função com x: int aceita tranquilamente uma string. Quem verifica as anotações são ferramentas externas — mypy, pyright, ty — e o editor, que destaca o erro antes mesmo de o programa ser executado.
def add_expense(title: str, amount: float, category: str = "outros") -> dict[str, object]:
return {"title": title, "amount": amount, "category": category}
def area(w: float, h: float) -> float:
w * h # mypy: Missing return statement
Para rodar a verificação sem instalar nada, use uvx mypy .. O exemplo acima mostra a utilidade das anotações: um return esquecido com -> float é apontado pelo analisador imediatamente.
A partir do Python 3.12 (PEP 695) funções genéricas são escritas sem TypeVar: o parâmetro de tipo vai entre colchetes, e um apelido de tipo é declarado com a palavra type.
def first[T](xs: list[T]) -> T:
return xs[0]
type Money = int | float
No Python 3.14 (PEP 649) as anotações passaram a ser preguiçosas: elas são calculadas no primeiro acesso, por isso é possível mencionar uma classe declarada mais abaixo sem aspas, e from __future__ import annotations não é mais necessário.
«Não foi passado» ou «foi passado None»: sentinel no 3.15
O valor padrão None não permite distinguir «o argumento não foi passado» de «foi passado None de propósito». Durante anos isso era resolvido com um marcador improvisado:
_MISSING = object()
def get_rate(currency, default=_MISSING):
rates = {"USD": 90.5}
if currency in rates:
return rates[currency]
if default is _MISSING:
raise KeyError(currency)
return default
No Python 3.15 apareceu a função embutida sentinel (PEP 661): MISSING = sentinel("MISSING") cria um marcador único com uma representação legível, que continua sendo ele mesmo ao ser copiado, é serializado pelo pickle e pode ser usado em uma anotação como float | MISSING. Em versões antigas continua valendo o recurso com object().
Erros típicos com funções
- Esqueceram o return — a função devolve
None. - Esqueceram os parênteses —
print(greet)mostra o objeto da função, e não o resultado da chamada. - Valor padrão mutável — uma lista compartilhada por todas as chamadas.
- Número errado de argumentos —
TypeError: greet() missing 1 required positional argument: 'name'. - Nome igual ao de um embutido — uma função chamada
listousumoculta a função original.
Resumo
Funções em Python são declaradas com def, devolvem o resultado com return e recebem argumentos por posição ou pelo nome. *args e **kwargs dão flexibilidade, / e * — rigor na chamada, o lambda — brevidade para funções descartáveis, e as anotações de tipo, junto com o mypy, pegam erros antes da execução. No curso de Python esse tema é praticado no dia a dia: o controle de gastos é dividido em funções com anotações e verificado pelo mypy.
Perguntas frequentes
Qual é a diferença entre parâmetro e argumento?
Parâmetro é o nome na declaração da função, e argumento é o valor concreto passado na chamada. Em def greet(name) o name é o parâmetro, e em greet("Ana") a string "Ana" é o argumento.
Dá para chamar uma função antes de declará-la?
No nível do módulo, não: a linha def precisa ser executada antes da chamada, senão ocorre NameError. Mas dentro de outra função é possível chamar uma função declarada mais abaixo, desde que a chamada aconteça depois da declaração.
Quantos valores uma função pode devolver?
Formalmente, um só, mas esse valor pode ser uma tupla: return a, b devolve uma tupla de dois elementos, que é desempacotada como x, y = f().
As anotações de tipo são obrigatórias?
Não, o Python funciona sem elas. Mas em código lido por outras pessoas elas facilitam a manutenção e permitem que mypy ou pyright encontrem erros antes da execução.
Posso usar if dentro de um lambda?
Só uma expressão condicional: lambda x: "par" if x % 2 == 0 else "ímpar". Instruções, laços e atribuições não são permitidos dentro de um lambda.
Este tema é abordado em profundidade, com prática, no curso “Python 3.15: от новичка до профи”.
Ir para o curso