Parâmetros Avançados e Escopo

Agora que você aprendeu o básico das funções, vamos explorar algumas técnicas avançadas. De quantas maneiras você pode escrever parâmetros? O que significa "escopo de variável"? O que é recursão? Estes conceitos levarão seu entendimento de funções ao próximo nível.


1. Parâmetros Padrão

Parâmetros padrão permitem omitir argumentos ao chamar uma função:

Exemplo: Parâmetros Padrão Básicos

PYTHON
def greet(name, greeting="Olá"):
    print(f"{greeting}, {name}!")

greet("Alice")              # Olá, Alice!
greet("Bob", "Oi")          # Oi, Bob!
greet("Charlie", "Ei")      # Ei, Charlie!
▶ Experimente

⚠️ A Armadilha do Parâmetro Padrão

Os valores dos parâmetros padrão são avaliados apenas uma vez, no momento da definição da função, não cada vez que a função é chamada. Se o padrão for um objeto mutável (como uma lista ou dicionário), isso pode levar a comportamento inesperado:

Exemplo: Armadilha do Padrão Mutável

PYTHON
def add_item(item, items=[]):   # ❌ Perigoso!
    items.append(item)
    return items

print(add_item("maçã"))     # ['maçã']
print(add_item("banana"))    # ['maçã', 'banana'] — não ['banana']!
print(add_item("laranja"))   # ['maçã', 'banana', 'laranja']
▶ Experimente

Por quê: A lista vazia items=[] é criada uma vez na definição da função. Cada chamada subsequente adiciona à mesma lista.

Abordagem correta: Use None como padrão e crie uma nova lista dentro da função:

Exemplo: Parâmetro Padrão Seguro

PYTHON
def add_item(item, items=None):  # ✅ Seguro
    if items is None:
        items = []
    items.append(item)
    return items

print(add_item("maçã"))     # ['maçã']
print(add_item("banana"))    # ['banana']
print(add_item("laranja"))   # ['laranja']
▶ Experimente
💡 Regra de ouro: Parâmetros padrão devem ser sempre valores imutáveis (None, números, strings, tuplas). Nunca use objetos mutáveis como listas ou dicionários como padrão.


2. Argumentos Nomeados (Keyword Arguments)

Ao chamar uma função, você pode especificar valores pelo nome do parâmetro, permitindo passar argumentos fora de ordem:

Exemplo: Argumentos Nomeados

PYTHON
def create_user(name, age, city):
    print(f"Usuário: {name}, {age} anos, de {city}")

# Argumentos posicionais — devem estar em ordem
create_user("Alice", 25, "São Paulo")

# Argumentos nomeados — especifique nomes dos parâmetros, ordem não importa
create_user(age=30, city="Rio de Janeiro", name="Bob")
create_user(name="Charlie", city="Belo Horizonte", age=22)

# Misturados — posicionais primeiro, nomeados depois
create_user("Diana", city="Salvador", age=28)
# create_user(name="Eve", 35, "Curtiba")   ❌ Args nomeados não podem preceder posicionais
▶ Experimente

3. *args: Argumentos Posicionais Variáveis

Se você não sabe quantos argumentos uma função receberá, use *args:

Exemplo: *args para Função de Soma

PYTHON
def sum_all(*numbers):
    """Aceita qualquer número de números e retorna sua soma"""
    total = 0
    for n in numbers:
        total += n
    return total

print(sum_all(1, 2))                # 3
print(sum_all(1, 2, 3, 4, 5))      # 15
print(sum_all())                    # 0 — sem argumentos também funciona
▶ Experimente

*args empacota todos os argumentos posicionais em uma tupla:

Exemplo: Inspecionando args

PYTHON
def show_args(*args):
    print(f"Recebidos {len(args)} argumentos: {args}")
    for i, arg in enumerate(args, 1):
        print(f"  #{i}: {arg}")

show_args("maçã", "banana", "laranja")
▶ Experimente

4. **kwargs: Argumentos Nomeados Variáveis

**kwargs aceita qualquer número de argumentos nomeados e os empacota em um dicionário:

Exemplo: **kwargs para Construir Perfis de Usuário

PYTHON
def create_profile(**info):
    """Cria um perfil de usuário, aceitando qualquer quantidade de informações"""
    print("Perfil do Usuário:")
    for key, value in info.items():
        print(f"  {key}: {value}")

create_profile(name="Alice", age=25, city="São Paulo", job="Engenheira")
▶ Experimente

Saída:

TEXT
Perfil do Usuário:
  name: Alice
  age: 25
  city: São Paulo
  job: Engenheira

Exemplo: Combinando *args e **kwargs

PYTHON
def advanced_function(a, b, *args, **kwargs):
    print(f"Posicionais: a={a}, b={b}")
    print(f"Posicionais extras: {args}")
    print(f"Nomeados: {kwargs}")

advanced_function(1, 2, 3, 4, 5, name="Alice", age=25)
# Posicionais: a=1, b=2
# Posicionais extras: (3, 4, 5)
# Nomeados: {'name': 'Alice', 'age': 25}
▶ Experimente
💡 A ordem dos parâmetros deve ser fixa: parâmetros normais → *args → parâmetros padrão → **kwargs


5. Escopo: Regra LEGB

Onde uma variável pode ser acessada? Isso é determinado pelo escopo. Python tem quatro níveis de escopo:

Exemplo: Aninhamento de Escopo LEGB

PYTHON
# Escopo global
x = 10          # Variável global

def outer():
    # Escopo da função envolvente
    x = 20      # Este é o x local de outer, diferente do x global
    
    def inner():
        # Escopo local
        x = 30  # Este é o x local de inner
        print(f"inner: {x}")     # 30
    
    inner()
    print(f"outer: {x}")         # 20

outer()
print(f"global: {x}")            # 10
▶ Experimente

LEGB significa:

Python busca variáveis na ordem L → E → G → B.

global e nonlocal

Para modificar uma variável global dentro de uma função, use global:

Exemplo: Usando global

PYTHON
count = 0

def increment():
    global count     # Declara intenção de modificar a variável global
    count += 1

increment()
increment()
print(count)         # 2
▶ Experimente
⚠️ Minimize o uso de global. Variáveis globais tornam o código mais difícil de entender e depurar. Funções devem receber entrada através de parâmetros e retornar saída através de return, não modificar secretamente variáveis globais.


6. Básico de Recursão

Recursão é quando uma função chama a si mesma. A ideia central: transforme um grande problema em subproblemas menores e similares.

Exemplo: Fatorial Recursivo

PYTHON
def factorial(n):
    """Calcula n! (fatorial de n)"""
    if n <= 1:
        return 1          # Condição de término
    return n * factorial(n - 1)   # Chamada recursiva

print(factorial(5))        # 120 (5×4×3×2×1)
▶ Experimente

Execução:

TEXT
factorial(5) = 5 * factorial(4)
            = 5 * 4 * factorial(3)
            = 5 * 4 * 3 * factorial(2)
            = 5 * 4 * 3 * 2 * factorial(1)
            = 5 * 4 * 3 * 2 * 1
            = 120

Exemplo: Sequência de Fibonacci (Dificuldade ⭐⭐⭐)

PYTHON
def fibonacci(n):
    """Retorna o enésimo número de Fibonacci (começando de 0)"""
    if n <= 1:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

# Imprime os primeiros 10
for i in range(10):
    print(fibonacci(i), end=" ")
# Saída: 0 1 1 2 3 5 8 13 21 34
▶ Experimente
💡 Dois elementos essenciais da recursão:Condição de término (senão recursão infinita → estouro de pilha). ② Condição recursiva (cada chamada se aproxima do término). Nem todos os problemas são adequados para recursão — se um laço funciona, prefira o laço. Recursão é ideal para estruturas de árvore, algoritmos de dividir e conquistar, etc.


Casos de Uso Comuns


❓ Perguntas Frequentes

P: Posso renomear *args e kwargs? R: Sim. *args e kwargs são apenas convenções de nomenclatura — os asteriscos são o que importa. Você poderia escrever *numbers, **options. Mas seguir a convenção é recomendado para que outros programadores Python entendam imediatamente. ⚠️ P: Recursão vs laços — qual é melhor? R: Se um laço pode resolver o problema, prefira o laço. Laços têm melhor desempenho, não causam estouro de pilha e são mais fáceis de entender. Recursão é adequada para dois cenários: travessia de estrutura de árvore (diretórios, JSON) e definições matematicamente recursivas (fatorial, Fibonacci). Recursão é mais concisa mas mais difícil de entender. P: Por que posso ler uma variável global dentro de uma função mas não modificá-la? R: Ao ler, Python encontra a variável global seguindo as regras LEGB. Mas ao atribuir, Python cria uma nova variável local por padrão — x = 10 dentro de uma função é tratado como "criar uma variável local x." Se você realmente precisa modificar uma global, use global. Mas isso geralmente significa que seu design poderia ser melhorado.


📖 Resumo


📝 Atividades

  1. Básico (Dificuldade ⭐): Escreva uma função multiply(*nums) que aceita qualquer número de números e retorna seu produto. Se nenhum argumento, retorne 0.

  2. Intermediário (Dificuldade ⭐⭐): Escreva uma função create_student(name, **scores) que recebe um nome de aluno e qualquer número de notas de disciplinas (argumentos nomeados), calcula e retorna a média. Exemplo de chamada: create_student("Alice", Português=85, Matemática=92, Inglês=78).

  3. Desafio (Dificuldade ⭐⭐⭐): Escreva uma função recursiva sum_digits(n) que calcula a soma dos dígitos de um inteiro positivo. Por exemplo, sum_digits(123) retorna 1+2+3=6. Dica: Módulo 10 dá o último dígito; divisão inteira por 10 o remove. Condição de término: n < 10.

100%