Python Admin

Guia Completo de Funções em Python: Definição, *args, **kwargs e Escopo Já leu

Introdução: Entendendo Funções em Python Funções são blocos de código reutilizáveis que executam uma tarefa específica. Em Python, elas são objetos de primeira classe, o que significa que podem ser atribuídas a variáveis, passadas como argumentos e retornadas de outras funções. Dominar funções é fundamental porque praticamente todo programa profissional as utiliza para organizar lógica, evitar repetição e criar código maintível. Nesta aula, você aprenderá não apenas como criar funções básicas, mas também técnicas avançadas como e , que permitem trabalhar com números variáveis de argumentos. Também exploraremos como Python gerencia escopos de variáveis, um conceito crítico que causa bugs silenciosos em código iniciante. Definição e Sintaxe Básica de Funções Criando Sua Primeira Função Uma função em Python começa com a palavra-chave , seguida pelo nome da função, parênteses e dois pontos. O corpo da função é indentado e pode conter múltiplas instruções. Opcionalmente, a função retorna um valor com . Observe que incluímos uma docstring (a string entre aspas

Introdução: Entendendo Funções em Python

Funções são blocos de código reutilizáveis que executam uma tarefa específica. Em Python, elas são objetos de primeira classe, o que significa que podem ser atribuídas a variáveis, passadas como argumentos e retornadas de outras funções. Dominar funções é fundamental porque praticamente todo programa profissional as utiliza para organizar lógica, evitar repetição e criar código maintível.

Nesta aula, você aprenderá não apenas como criar funções básicas, mas também técnicas avançadas como *args e **kwargs, que permitem trabalhar com números variáveis de argumentos. Também exploraremos como Python gerencia escopos de variáveis, um conceito crítico que causa bugs silenciosos em código iniciante.

Definição e Sintaxe Básica de Funções

Criando Sua Primeira Função

Uma função em Python começa com a palavra-chave def, seguida pelo nome da função, parênteses e dois pontos. O corpo da função é indentado e pode conter múltiplas instruções. Opcionalmente, a função retorna um valor com return.

def calcular_media(nota1, nota2, nota3):
    """Calcula a média aritmética de três notas."""
    media = (nota1 + nota2 + nota3) / 3
    return media

resultado = calcular_media(7.5, 8.0, 9.5)
print(resultado)  # Saída: 8.333...

Observe que incluímos uma docstring (a string entre aspas triples). Essa é uma boa prática que documenta o propósito da função. A função recebe parâmetros (nota1, nota2, nota3) e retorna um valor. Sem return explícito, Python retorna None.

Argumentos Padrão e Argumentos Posicionais

Você pode definir valores padrão para parâmetros. Argumentos com valores padrão devem vir após argumentos sem valores padrão.

def saudar(nome, sobrenome="Silva", prefixo="Sr./Sra."):
    """Cria uma saudação personalizada."""
    return f"{prefixo} {nome} {sobrenome}"

print(saudar("João"))  # Sr./Sra. João Silva
print(saudar("Maria", "Santos"))  # Sr./Sra. Maria Santos
print(saudar("Pedro", "Costa", "Dr."))  # Dr. Pedro Costa

Quando você chama a função, pode usar argumentos posicionais (na ordem) ou argumentos nomeados (em qualquer ordem).

print(saudar(nome="Ana", prefixo="Dra."))  # Dra. Ana Silva
print(saudar(prefixo="Prof.", nome="Carlos", sobrenome="Oliveira"))
# Prof. Carlos Oliveira

*args: Número Variável de Argumentos Posicionais

Por que Usar *args?

Às vezes, você não sabe quantos argumentos uma função receberá. Imagine uma função que calcula a soma de qualquer quantidade de números. Sem *args, você teria que criar versões diferentes da função. Com *args, você resolve isso elegantemente.

*args é uma convenção de nomenclatura (o importante é o asterisco, não o nome "args"). Ele coleta todos os argumentos posicionais extras em uma tupla. A função consegue iterar sobre essa tupla.

def somar(*numeros):
    """Soma qualquer quantidade de números."""
    total = 0
    for numero in numeros:
        total += numero
    return total

print(somar(1, 2, 3))  # 6
print(somar(10, 20, 30, 40, 50))  # 150
print(somar())  # 0 (tupla vazia)

Você pode verificar que numeros é uma tupla:

def inspecionar(*args):
    print(f"Tipo: {type(args)}")
    print(f"Conteúdo: {args}")

inspecionar(1, "olá", 3.14)
# Tipo: <class 'tuple'>
# Conteúdo: (1, 'olá', 3.14)

Combinando Argumentos Normais com *args

Frequentemente, você quer alguns argumentos obrigatórios seguidos de argumentos variáveis. A ordem importa: argumentos normais primeiro, depois *args.

def criar_relatorio(titulo, *dados):
    """Cria um relatório com um título e múltiplos dados."""
    print(f"=== {titulo} ===")
    for i, dado in enumerate(dados, start=1):
        print(f"{i}. {dado}")

criar_relatorio("Vendas Mensais", 1500, 2300, 1800, 2100)
# === Vendas Mensais ===
# 1. 1500
# 2. 2300
# 3. 1800
# 4. 2100

**kwargs: Número Variável de Argumentos Nomeados

Entendendo **kwargs

**kwargs (keyword arguments) funciona como *args, mas para argumentos nomeados. Ele coleta todos os argumentos nomeados em um dicionário. Isso é especialmente útil quando você precisa passar configurações ou opções variáveis.

def configurar_servidor(**opcoes):
    """Configura um servidor com opções variáveis."""
    for chave, valor in opcoes.items():
        print(f"{chave}: {valor}")

configurar_servidor(host="localhost", porta=8080, debug=True, timeout=30)
# host: localhost
# porta: 8080
# debug: True
# timeout: 30

A diferença fundamental: *args é uma tupla (ordenada), **kwargs é um dicionário (chave-valor).

Combinando args, *kwargs e Argumentos Normais

Você pode usar os três tipos juntos, mas a ordem é crucial: argumentos normais → *args**kwargs.

def processar_pedido(cliente, *itens, **opcoes):
    """Processa um pedido com cliente, itens e opções."""
    print(f"Cliente: {cliente}")
    print(f"Itens: {', '.join(itens)}")
    print("Opções:")
    for chave, valor in opcoes.items():
        print(f"  {chave}: {valor}")

processar_pedido(
    "João Silva",
    "Notebook", "Mouse", "Teclado",
    frete="expressa",
    garantia="24 meses",
    desconto=0.10
)
# Cliente: João Silva
# Itens: Notebook, Mouse, Teclado
# Opções:
#   frete: expressa
#   garantia: 24 meses
#   desconto: 0.10

Desempacotamento com * e **

Você também pode usar * e ** ao chamar uma função, para desempacotar iteráveis e dicionários.

def adicionar(a, b, c):
    return a + b + c

numeros = [1, 2, 3]
print(adicionar(*numeros))  # 6, desempacota a lista

opcoes = {"a": 10, "b": 20, "c": 30}
print(adicionar(**opcoes))  # 60, desempacota o dicionário

Escopo de Variáveis

Os Quatro Níveis de Escopo: LEGB

Python utiliza a regra LEGB para resolver nomes de variáveis. Quando você referencia uma variável, Python procura nesta ordem: Local → Enclosing → Global → Built-in.

  • Local (L): Variáveis dentro da função atual
  • Enclosing (E): Variáveis em funções externas (para funções aninhadas)
  • Global (G): Variáveis no nível do módulo/script
  • Built-in (B): Nomes pré-definidos do Python (print, len, etc.)
x = "global"

def funcao_externa():
    x = "enclosing"

    def funcao_interna():
        x = "local"
        print(f"Dentro de funcao_interna: {x}")

    funcao_interna()
    print(f"Dentro de funcao_externa: {x}")

funcao_externa()
print(f"No nível global: {x}")

# Dentro de funcao_interna: local
# Dentro de funcao_externa: enclosing
# No nível global: global

Modificando Variáveis Globais: global e nonlocal

Por padrão, se você tenta atribuir um valor a uma variável dentro de uma função, Python cria uma nova variável local, não modifica a global. Use a palavra-chave global para referir-se à variável global.

contador = 0

def incrementar():
    global contador
    contador += 1

incrementar()
incrementar()
print(contador)  # 2

Para funções aninhadas, use nonlocal para acessar variáveis do escopo envolvente (não global).

def externa():
    valor = 10

    def interna():
        nonlocal valor
        valor += 5
        print(valor)

    interna()
    print(valor)

externa()
# 15
# 15

Escopo e Funções Aninhadas (Closures)

Funções internas podem "capturar" variáveis do escopo externo, criando closures. Isso é poderoso para criar funções parametrizadas.

def criar_multiplicador(fator):
    """Retorna uma função que multiplica por um fator."""
    def multiplicar(numero):
        return numero * fator
    return multiplicar

vezes_3 = criar_multiplicador(3)
vezes_5 = criar_multiplicador(5)

print(vezes_3(10))  # 30
print(vezes_5(10))  # 50

A função multiplicar captura a variável fator do escopo externo. Cada vez que você chama criar_multiplicador, uma nova closure é criada com seu próprio fator.

Exemplo Prático Integrado

Para consolidar tudo que aprendemos, aqui está uma função que combina os conceitos:

historico_global = []

def registrar_operacao(operacao, *valores, usuario="sistema", **metadados):
    """Registra uma operação com valores variáveis e metadados."""
    global historico_global

    resultado = None

    if operacao == "soma":
        resultado = sum(valores)
    elif operacao == "multiplicacao":
        resultado = 1
        for v in valores:
            resultado *= v

    registro = {
        "operacao": operacao,
        "valores": valores,
        "resultado": resultado,
        "usuario": usuario,
        "metadados": metadados
    }

    historico_global.append(registro)
    return resultado

# Exemplos de uso
registrar_operacao("soma", 1, 2, 3, usuario="admin", timestamp="2024-01-15")
registrar_operacao("multiplicacao", 2, 3, 4, prioridade="alta")
registrar_operacao("soma", 10, 20)

for registro in historico_global:
    print(f"{registro['usuario']}: {registro['operacao']} = {registro['resultado']}")

Este exemplo demonstra:
- Argumentos normais (operacao)
- *valores para múltiplos argumentos posicionais
- Argumentos com padrão (usuario="sistema")
- **metadados para configurações variáveis
- Uso de variável global (historico_global)
- Lógica condicional e processamento

Conclusão

Ao dominar funções em Python, você adquire ferramentas essenciais para escrever código profissional e mantível. Três pontos-chave a lembrar: Primeiro, *args e **kwargs não são obrigatórios para toda função, mas são inestimáveis quando você precisa flexibilidade com argumentos variáveis — use-os conscientemente para APIs e funções utilitárias. Segundo, compreender a regra LEGB e as palavras-chave global e nonlocal evita bugs silenciosos que surgem quando você inadvertidamente cria variáveis locais pensando estar modificando globais. Terceiro, closures e funções aninhadas abrem possibilidades de programação funcional e design patterns avançados — explore-as para expandir sua capacidade de abstração.

Referências


Artigos relacionados