Funções: definindo, chamando e retornando valores

Funções são blocos de código reutilizáveis que executam uma tarefa específica. Em Python, elas são fundamentais para organizar, modularizar e dar clareza ao código. Este artigo aborda desde a definição básica até conceitos avançados, com exemplos práticos.

1. Definindo Funções em Python

A sintaxe para definir uma função em Python utiliza a palavra-chave def, seguida do nome da função, parênteses e dois pontos. O corpo da função deve ser indentado (geralmente com 4 espaços).

def saudacao():
    """Exibe uma mensagem de saudação."""
    print("Olá, mundo!")

A docstring (entre """ """) é opcional, mas altamente recomendada. Ela documenta o propósito da função e pode ser acessada com help(nome_da_funcao) ou nome_da_funcao.__doc__.

def calcular_media(nota1, nota2):
    """
    Calcula a média aritmética de duas notas.

    Args:
        nota1 (float): Primeira nota.
        nota2 (float): Segunda nota.

    Returns:
        float: Média das notas.
    """
    return (nota1 + nota2) / 2

2. Chamando Funções

Para executar uma função, basta usar seu nome seguido de parênteses. Se a função espera argumentos, eles devem ser fornecidos.

# Chamada simples
saudacao()  # Saída: Olá, mundo!

# Chamada com argumentos
media = calcular_media(7.5, 8.2)
print(media)  # Saída: 7.85

A ordem de execução segue a pilha de chamadas (call stack). Quando uma função é chamada, ela é empilhada; ao finalizar, é desempilhada e o controle retorna ao ponto de chamada.

def funcao_a():
    print("Entrando em A")
    funcao_b()
    print("Saindo de A")

def funcao_b():
    print("Entrando em B")
    print("Saindo de B")

funcao_a()
# Saída:
# Entrando em A
# Entrando em B
# Saindo de B
# Saindo de A

3. Parâmetros e Argumentos

Parâmetros obrigatórios (posicionais)

São aqueles que devem ser fornecidos na ordem correta durante a chamada.

def exibir_dados(nome, idade, cidade):
    print(f"{nome} tem {idade} anos e mora em {cidade}.")

exibir_dados("Ana", 28, "São Paulo")

Parâmetros com valores padrão

Valores padrão tornam o parâmetro opcional. Eles são avaliados apenas uma vez, no momento da definição.

def cadastro(nome, email, ativo=True):
    if ativo:
        print(f"{nome} ({email}) - Ativo")
    else:
        print(f"{nome} ({email}) - Inativo")

cadastro("João", "joao@email.com")
cadastro("Maria", "maria@email.com", False)

Passagem por posição vs. por nome

Argumentos podem ser passados por posição (na ordem dos parâmetros) ou por nome (usando o nome do parâmetro).

def configurar(tema, idioma, notificacoes=True):
    print(f"Tema: {tema}, Idioma: {idioma}, Notificações: {notificacoes}")

# Por posição
configurar("escuro", "pt-BR", False)

# Por nome (ordem não importa)
configurar(idioma="en", tema="claro")

# Misto (posicionais primeiro, depois nomeados)
configurar("escuro", notificacoes=False, idioma="es")

4. Retornando Valores com return

Uso básico

A palavra-chave return encerra a execução da função e retorna um valor ao chamador.

def somar(a, b):
    return a + b

resultado = somar(10, 20)
print(resultado)  # Saída: 30

Retornando múltiplos valores

Python permite retornar múltiplos valores como uma tupla (implícita ou explícita).

def dividir(dividendo, divisor):
    quociente = dividendo // divisor
    resto = dividendo % divisor
    return quociente, resto  # Retorna uma tupla

q, r = dividir(17, 5)
print(f"Quociente: {q}, Resto: {r}")  # Saída: Quociente: 3, Resto: 2

Funções sem return

Se uma função não possui return, ela retorna None implicitamente.

def mostrar_mensagem():
    print("Esta função não retorna nada explicitamente.")

resultado = mostrar_mensagem()
print(resultado)  # Saída: None

5. Escopo de Variáveis em Funções

Variáveis locais vs. globais

Variáveis definidas dentro de uma função são locais — existem apenas durante a execução da função. Variáveis definidas fora são globais e podem ser acessadas, mas não modificadas diretamente dentro da função.

x = 10  # Variável global

def funcao():
    y = 5  # Variável local
    print(f"Dentro: x = {x}, y = {y}")

funcao()
print(f"Fora: x = {x}")
# print(y)  # Erro! y não existe fora da função

A palavra-chave global

Para modificar uma variável global dentro de uma função, é necessário declarar global.

contador = 0

def incrementar():
    global contador
    contador += 1
    print(f"Contador: {contador}")

incrementar()  # Saída: Contador: 1
incrementar()  # Saída: Contador: 2

Acesso a variáveis do escopo externo

Funções aninhadas podem acessar variáveis do escopo externo (nonlocal).

def externa():
    mensagem = "Olá"

    def interna():
        print(mensagem)  # Acessa variável do escopo externo

    interna()

externa()  # Saída: Olá

6. Funções como Objetos de Primeira Classe

Em Python, funções são objetos de primeira classe: podem ser atribuídas a variáveis, passadas como argumentos e retornadas por outras funções.

Atribuindo funções a variáveis

def quadrado(n):
    return n ** 2

minha_funcao = quadrado
print(minha_funcao(5))  # Saída: 25

Passando funções como argumentos

def aplicar_operacao(operacao, a, b):
    return operacao(a, b)

def somar(x, y):
    return x + y

def multiplicar(x, y):
    return x * y

print(aplicar_operacao(somar, 3, 4))       # Saída: 7
print(aplicar_operacao(multiplicar, 3, 4)) # Saída: 12

Funções aninhadas (inner functions)

Funções podem ser definidas dentro de outras funções.

def criar_saudacao(saudacao):
    def saudar(nome):
        return f"{saudacao}, {nome}!"
    return saudar

saudar_oi = criar_saudacao("Oi")
saudar_ola = criar_saudacao("Olá")

print(saudar_oi("Ana"))   # Saída: Oi, Ana!
print(saudar_ola("João")) # Saída: Olá, João!

7. Boas Práticas com Funções

Nomes descritivos e verbos

Use verbos ou frases verbais que descrevam a ação da função.

# Ruim
def calc(x, y):
    return x * y

# Bom
def calcular_area_retangulo(largura, altura):
    return largura * altura

Funções pequenas e com responsabilidade única

Cada função deve fazer apenas uma coisa bem feita. Se uma função está fazendo múltiplas tarefas, considere dividi-la.

# Ruim: faz validação, processamento e formatação
def processar_usuario(dados):
    if not dados.get("email"):
        return "Erro"
    nome = dados["nome"].upper()
    return f"USUÁRIO: {nome}"

# Bom: funções separadas
def validar_dados(dados):
    return "email" in dados

def formatar_nome(nome):
    return nome.upper()

def processar_usuario(dados):
    if not validar_dados(dados):
        return "Erro"
    return f"USUÁRIO: {formatar_nome(dados['nome'])}"

Evitando efeitos colaterais (side effects)

Prefira funções que recebem dados e retornam resultados, sem modificar variáveis globais ou argumentos mutáveis.

# Ruim: modifica a lista original
def adicionar_item_ruim(item, lista):
    lista.append(item)

# Bom: retorna uma nova lista
def adicionar_item_bom(item, lista):
    return lista + [item]

original = [1, 2, 3]
adicionar_item_ruim(4, original)
print(original)  # Saída: [1, 2, 3, 4] - modificada!

original = [1, 2, 3]
nova = adicionar_item_bom(4, original)
print(original)  # Saída: [1, 2, 3] - preservada
print(nova)      # Saída: [1, 2, 3, 4]

Conclusão

Funções são o bloco de construção fundamental para código Python organizado e reutilizável. Dominar definição, chamada, parâmetros, retorno, escopo e boas práticas permite escrever programas mais claros, modulares e fáceis de manter. Pratique criando funções para tarefas do dia a dia e explore conceitos mais avançados como decoradores, geradores e closures.

Referências