Funções como objetos: passando e retornando funções
1. Funções são Objetos de Primeira Classe em Python
Em Python, funções são cidadãs de primeira classe — isso significa que podem ser tratadas como qualquer outro objeto: atribuídas a variáveis, armazenadas em estruturas de dados e passadas como argumentos. Essa característica é fundamental para o paradigma funcional da linguagem.
def saudacao(nome):
return f"Olá, {nome}!"
# Atribuindo função a uma variável
minha_funcao = saudacao
print(minha_funcao("Ana")) # Olá, Ana!
print(minha_funcao.__name__) # saudacao
print(id(saudacao) == id(minha_funcao)) # True
Funções podem ser armazenadas em listas e dicionários:
def somar(a, b): return a + b
def subtrair(a, b): return a - b
def multiplicar(a, b): return a * b
operacoes = {
'+': somar,
'-': subtrair,
'*': multiplicar
}
print(operacoes['+'](10, 5)) # 15
print(operacoes['*'](10, 5)) # 50
2. Passando Funções como Argumentos
Passar funções como argumentos permite criar código flexível e reutilizável. O exemplo clássico é o parâmetro key de funções como sorted() e max():
nomes = ["Alice", "bob", "Charlie", "david"]
print(sorted(nomes, key=len)) # ['bob', 'Alice', 'david', 'Charlie']
print(sorted(nomes, key=str.lower)) # ['Alice', 'bob', 'Charlie', 'david']
Podemos criar nosso próprio filtro genérico:
def filtrar(lista, criterio):
"""Filtra elementos de uma lista usando uma função de teste."""
return [item for item in lista if criterio(item)]
def maior_que_10(x): return x > 10
def par(x): return x % 2 == 0
numeros = [3, 15, 8, 22, 7, 14]
print(filtrar(numeros, maior_que_10)) # [15, 22, 14]
print(filtrar(numeros, par)) # [8, 22, 14]
3. Retornando Funções de Outras Funções (Closures)
Uma closure é uma função interna que "captura" variáveis do escopo externo, mesmo após a função externa ter terminado sua execução:
def multiplicador(n):
"""Retorna uma função que multiplica qualquer valor por n."""
def multiplicar(x):
return x * n
return multiplicar
dobro = multiplicador(2)
triplo = multiplicador(3)
print(dobro(10)) # 20
print(triplo(10)) # 30
print(dobro.__closure__[0].cell_contents) # 2
Isso permite encapsular estado de forma elegante e criar funções com comportamento parametrizado:
def contador():
"""Closure que mantém estado interno."""
valor = 0
def incrementar():
nonlocal valor
valor += 1
return valor
return incrementar
meu_contador = contador()
print(meu_contador()) # 1
print(meu_contador()) # 2
print(meu_contador()) # 3
4. Decorators: Aplicação Prática de Funções como Objetos
Decorators são funções que recebem outra função e retornam uma versão modificada dela. É uma aplicação direta do conceito de funções como objetos:
import time
def medir_tempo(func):
"""Decorator que mede o tempo de execução de uma função."""
def wrapper(*args, **kwargs):
inicio = time.time()
resultado = func(*args, **kwargs)
fim = time.time()
print(f"{func.__name__} executou em {fim - inicio:.4f}s")
return resultado
return wrapper
@medir_tempo
def calcular_fatorial(n):
from math import factorial
return factorial(n)
print(calcular_fatorial(100))
Decorators com argumentos requerem um nível extra de aninhamento:
def log_com_prefixo(prefixo):
"""Decorator com argumento para customizar o log."""
def decorator(func):
def wrapper(*args, **kwargs):
print(f"[{prefixo}] Executando {func.__name__}")
return func(*args, **kwargs)
return wrapper
return decorator
@log_com_prefixo("INFO")
def processar_dados():
return "Processamento concluído"
print(processar_dados())
5. Funções de Alta Ordem da Biblioteca Padrão
Python oferece funções de alta ordem prontas na biblioteca padrão:
from functools import reduce, partial
# map: transforma cada elemento
numeros = [1, 2, 3, 4, 5]
quadrados = list(map(lambda x: x**2, numeros))
print(quadrados) # [1, 4, 9, 16, 25]
# filter: seleciona elementos que satisfazem condição
pares = list(filter(lambda x: x % 2 == 0, numeros))
print(pares) # [2, 4]
# reduce: reduz sequência a um único valor
soma = reduce(lambda a, b: a + b, numeros)
print(soma) # 15
# partial: fixa argumentos de uma função
def potencia(base, expoente):
return base ** expoente
quadrado = partial(potencia, expoente=2)
cubo = partial(potencia, expoente=3)
print(quadrado(5)) # 25
print(cubo(5)) # 125
6. Composição de Funções
Compor funções é criar novas funções a partir da combinação de outras:
def compor(f, g):
"""Retorna f(g(x))."""
return lambda x: f(g(x))
def dobrar(x): return x * 2
def somar_5(x): return x + 5
resultado = compor(dobrar, somar_5)
print(resultado(10)) # 30 (dobrar(somar_5(10)) = 30)
# Pipe operator funcional
def pipeline(*funcoes):
"""Executa funções em sequência, passando resultado adiante."""
def aplicar(valor):
for func in funcoes:
valor = func(valor)
return valor
return aplicar
def limpar(texto): return texto.strip().lower()
def remover_espacos(texto): return texto.replace(" ", "_")
def adicionar_prefixo(texto): return f"dados_{texto}"
processar = pipeline(limpar, remover_espacos, adicionar_prefixo)
print(processar(" Exemplo de DADOS ")) # dados_exemplo_de_dados
7. Considerações de Performance e Boas Práticas
O uso de funções como objetos traz flexibilidade, mas é importante considerar o equilíbrio entre expressividade e performance:
from typing import Callable, List, TypeVar
T = TypeVar('T')
# Type hints melhoram legibilidade e documentação
def aplicar_transformacao(
dados: List[T],
transformacao: Callable[[T], T]
) -> List[T]:
"""Aplica uma transformação a cada elemento da lista."""
return [transformacao(item) for item in dados]
# Prefira funções nomeadas para lógica complexa
def processar_cliente(cliente: dict) -> dict:
"""Processa dados de um cliente (lógica complexa)."""
cliente['nome'] = cliente['nome'].title()
cliente['email'] = cliente['email'].lower()
return cliente
clientes = [
{'nome': 'ana silva', 'email': 'ANA@exemplo.com'},
{'nome': 'CARLOS SOUZA', 'email': 'carlos@exemplo.com'}
]
resultados = aplicar_transformacao(clientes, processar_cliente)
print(resultados)
Para operações simples em grandes volumes de dados, loops tradicionais podem ser mais rápidos que closures e funções aninhadas. Use funções como objetos onde a legibilidade e reutilização são prioridades.
Referências
- Python Documentation: Functions as First-Class Objects — Documentação oficial sobre definição e uso de funções em Python
- Real Python: Python Closures — Tutorial completo sobre closures e escopo léxico em Python
- PEP 318 – Decorators for Functions and Methods — Proposta oficial que introduziu decorators na linguagem Python
- Python Documentation: functools Module — Documentação do módulo functools com partial, reduce e outras funções de alta ordem
- GeeksforGeeks: First Class Functions in Python — Artigo técnico explicando funções como objetos de primeira classe com exemplos práticos
- Python Documentation: typing.Callable — Documentação oficial sobre type hints para funções com Callable