Funções são blocos de código reutilizáveis que executam uma tarefa específica. Em Python, funções são definidas com a palavra-chave def e são fundamentais para organizar programas em partes lógicas, evitar repetição e facilitar a manutenção. Nesta aula, vamos explorar os principais aspectos de funções: como defini-las, passar parâmetros, retornar valores, documentá-las adequadamente e entender como o escopo de variáveis funciona.

Dominar funções é essencial para qualquer programador Python, pois elas permitem criar código modular, testável e legível. Vamos começar com a sintaxe básica e evoluir para conceitos mais avançados.

def

A palavra-chave def é usada para definir uma função. A sintaxe básica é:

def nome_da_funcao(parametros):
    # corpo da função
    # pode conter várias linhas
    return valor_opcional

O nome da função deve seguir as regras de identificadores (geralmente letras minúsculas com underscores para separar palavras). A definição é seguida por parênteses que podem conter parâmetros (opcionais) e dois-pontos. O corpo deve ser indentado (geralmente 4 espaços). A função pode ou não retornar um valor com return. Se não houver return, a função retorna None implicitamente.

def saudacao():
    print("Olá, mundo!")

saudacao()  # Chama a função

Funções podem ser chamadas quantas vezes forem necessárias, tornando o código mais limpo e evitando duplicação.

Parâmetros e retorno

Parâmetros são valores que a função recebe para trabalhar. Eles são definidos entre parênteses na declaração da função. Os argumentos são os valores passados na chamada. Python suporta vários tipos de parâmetros:

  • Parâmetros posicionais: a ordem importa.
  • Parâmetros nomeados (keyword arguments): passados com nome, permitindo ordem diferente.
  • Valores padrão: você pode definir um valor padrão para um parâmetro, tornando-o opcional.
  • *args e **kwargs: para número variável de argumentos posicionais e nomeados.
def soma(a, b):
    return a + b

print(soma(2, 3))          # 5
print(soma(b=4, a=1))      # 5 (nomeados)

def cumprimentar(nome, saudacao="Olá"):
    return f"{saudacao}, {nome}!"

print(cumprimentar("Maria"))                # Olá, Maria!
print(cumprimentar("João", "Oi"))          # Oi, João!

O retorno é feito com a instrução return. Uma função pode retornar múltiplos valores como tupla. O return também encerra a execução da função imediatamente.

def dividir(a, b):
    quociente = a // b
    resto = a % b
    return quociente, resto

q, r = dividir(10, 3)
print(q, r)  # 3 1

Docstrings

Docstrings são strings de documentação que descrevem o propósito da função, seus parâmetros e o que retorna. Elas são colocadas imediatamente após a linha def antes do corpo, entre aspas triplas. São acessíveis via help(nome_da_funcao) ou nome_da_funcao.__doc__. Boas docstrings seguem convenções como a PEP 257.

def soma(a, b):
    """
    Retorna a soma de dois números.

    Args:
        a (int/float): Primeiro número.
        b (int/float): Segundo número.

    Returns:
        int/float: A soma de a e b.
    """
    return a + b

print(soma.__doc__)

Docstrings são importantes para documentação automática e para outros programadores entenderem sua função sem ler o código.

Escopo

Escopo refere-se à visibilidade e ao tempo de vida das variáveis. Em Python, variáveis definidas dentro de uma função têm escopo local, ou seja, não podem ser acessadas fora dela. Variáveis definidas fora de qualquer função têm escopo global. Para modificar uma variável global dentro de uma função, usamos a palavra-chave global (ou nonlocal para escopo aninhado).

x = 10  # global

def modificar_global():
    global x
    x = 20

modificar_global()
print(x)  # 20

É importante entender que, por padrão, atribuir a uma variável dentro de uma função cria uma variável local, mesmo que exista uma global com o mesmo nome. Isso evita efeitos colaterais indesejados. Boa prática: evitar o uso excessivo de variáveis globais, preferindo passar valores como parâmetros e retornar resultados.

def exemplo_escopo():
    y = 5  # local
    return y

print(exemplo_escopo())  # 5
# print(y)  # NameError: name 'y' is not defined

Boas práticas e observações finais

Ao escrever funções, siga estas recomendações:

  • Use nomes descritivos e em minúsculas com palavras separadas por underscore.
  • Mantenha as funções pequenas e com responsabilidade única.
  • Sempre documente com docstrings.
  • Evite variáveis globais, prefira parâmetros e retornos.
  • Use return consistente: ou sempre retorne algo, ou sempre não retorne.
  • Prefira funções puras (sem efeitos colaterais) sempre que possível.

Essas práticas tornam o código mais legível, testável e fácil de manter.

Exercícios

  1. Exercício 1: Escreva uma função chamada par_ou_impar que recebe um número e retorna a string "par" se o número for par, e "ímpar" se for ímpar. Teste com alguns valores.

    ✓ Resposta:
    def par_ou_impar(num):
        if num % 2 == 0:
            return "par"
        else:
            return "ímpar"
    
    print(par_ou_impar(4))  # par
    print(par_ou_impar(7))  # ímpar
    
  2. Exercício 2: Crie uma função area_retangulo que recebe largura e altura e retorna a área. Adicione uma docstring adequada. Teste com valores decimais.

    ✓ Resposta:
    def area_retangulo(largura, altura):
        """
        Calcula a área de um retângulo.
    
        Args:
            largura (float): Largura do retângulo.
            altura (float): Altura do retângulo.
    
        Returns:
            float: Área calculada (largura * altura).
        """
        return largura * altura
    
    print(area_retangulo(5.0, 3.0))  # 15.0
    
  3. Exercício 3: Escreva uma função contagem_regressiva que recebe um número n e imprime uma contagem regressiva de n até 1, e depois imprime "Lançar!". Use um loop while. Não retorne nada.

    ✓ Resposta:
    def contagem_regressiva(n):
        while n > 0:
            print(n)
            n -= 1
        print("Lançar!")
    
    contagem_regressiva(3)
    
  4. Exercício 4: Defina uma função soma_lista que recebe uma lista de números e retorna a soma deles. Use um loop for. Não use a função sum().

    ✓ Resposta:
    def soma_lista(lista):
        total = 0
        for num in lista:
            total += num
        return total
    
    print(soma_lista([1, 2, 3, 4]))  # 10
    
  5. Exercício 5: Explique o que o código abaixo imprime e por quê. Considere o escopo das variáveis.
    x = 50
    
    def func():
        x = 10
        return x
    
    print(x)      # ?
    print(func()) # ?
    print(x)      # ?
    

    ✓ Resposta: Imprime 50, 10, 50. A primeira impressão mostra a variável global x (50). Dentro da função, a atribuição x = 10 cria uma variável local, então a função retorna 10. Após a chamada da função, a variável global x permanece inalterada, então a terceira impressão é 50. Isso demonstra que atribuições dentro de funções não afetam variáveis globais a menos que usemos global.

Referências