Fundamentos

Funções em Python: def, return, argumentos, *args e **kwargs, lambda e anotações de tipo

Atualizado: · Skillaria.Top

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.

deflambda
Nometemnão tem (<lambda>)
Corpoqualquer número de instruçõesuma expressão
returnexplícitoimplícito
Docstring, anotaçõessimnão
Onde cabeem qualquer lugarkey 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 list ou sum oculta 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.

Aprenda este tema em vídeo

Este tema é abordado em profundidade, com prática, no curso “Python 3.15: от новичка до профи”.

Ir para o curso