Python & AI Tutorials Logo
Programação Python

40. Escrevendo Código Limpo e Legível

Ao longo deste livro, você aprendeu a sintaxe do Python, estruturas de dados, fluxo de controle, funções, classes e muitos outros conceitos de programação. Agora você consegue escrever programas que funcionam. Mas existe uma diferença crucial entre código que funciona e código que é manutenível — código que você e outras pessoas conseguem entender, modificar e depurar meses ou anos depois.

Este capítulo foca em escrever código limpo e legível. Você vai aprender as convenções e práticas que tornam código Python profissional e manutenível. Isso não são apenas regras arbitrárias — são diretrizes testadas na prática que facilitam a colaboração, reduzem bugs e ajudam você a entender o seu próprio código quando voltar a ele mais tarde.

40.1) Por que Estilo Importa: Ler vs. Escrever Código

40.1.1) Código é Lido com Mais Frequência do que Escrito

Quando você escreve código, você passa minutos ou horas criando-o. Mas esse código será lido muitas vezes: quando você depura, quando adiciona funcionalidades, quando outros desenvolvedores trabalham com ele e quando você volta a ele meses depois tentando lembrar o que ele faz.

Considere este código que funciona, mas tem um estilo ruim:

python
# AVISO: Estilo ruim - apenas para demonstração
def c(l):
    t=0
    for i in l:
        t=t+i
    return t/len(l)
 
data=[85,92,78,90,88]
result=c(data)
print(result)  # Output: 86.6

Esse código funciona perfeitamente. Ele calcula a média de uma lista de números. Mas entender o que ele faz exige uma análise cuidadosa. Agora compare com esta versão:

python
def calculate_average(numbers):
    """Calculate the arithmetic mean of a list of numbers."""
    total = 0
    for number in numbers:
        total = total + number
    return total / len(numbers)
 
test_scores = [85, 92, 78, 90, 88]
average_score = calculate_average(test_scores)
print(average_score)  # Output: 86.6

O que torna a segunda versão melhor?

  • O nome da função (calculate_average) deixa o propósito claro
  • Os nomes das variáveis (numbers, total, test_scores) são descritivos
  • A docstring explica o que a função faz
  • O espaçamento adequado deixa a estrutura clara
  • Qualquer pessoa consegue entender esse código sem estudá-lo

As duas versões produzem resultados idênticos, mas a segunda versão é imediatamente compreensível.

O insight principal: você escreve código uma vez, mas você o lê dezenas ou centenas de vezes. Investir alguns segundos a mais em nomes e formatação claros economiza horas de confusão depois.

40.1.2) Legibilidade Reduz Bugs

Código claro é mais fácil de depurar porque você consegue entender rapidamente o que cada parte faz. Quando os nomes de variáveis são descritivos e a estrutura é limpa, você consegue identificar erros de lógica com mais facilidade.

python
# Difícil de depurar - o que essas variáveis representam?
# AVISO: Estilo ruim - apenas para demonstração
def process(x, y):
    if x > y:
        return x * (1 - y)
    return x
 
result = process(100, 0.1)
python
# Fácil de depurar - fica claro o que está acontecendo
def apply_discount(price, discount_rate):
    """Calculate price after applying discount rate (0.0 to 1.0)."""
    discount_amount = price * discount_rate
    final_price = price - discount_amount
    return final_price
 
original_price = 100
discount = 0.1  # desconto de 10%
final_price = apply_discount(original_price, discount)
print(f"Final price: ${final_price}")
# Output: Final price: $90.0

Na segunda versão, você consegue ver imediatamente a lógica: "Estamos calculando um valor de desconto e depois subtraindo esse valor do preço." Na primeira versão, você precisa acompanhar mentalmente o que x e y representam e descobrir o que x * (1 - y) significa.

40.1.3) Consistência Permite Colaboração

Quando todo mundo em um time segue as mesmas convenções de estilo, o código fica previsível. Você não gasta energia mental decifrando estilos de formatação diferentes — você pode focar em entender a lógica.

O Python tem um guia de estilo oficial chamado PEP 8 (Python Enhancement Proposal 8). A PEP 8 define convenções para:

  • Como nomear variáveis, funções e classes
  • Como formatar código (espaçamento, comprimento de linha, indentação)
  • Quando usar comentários e docstrings
  • Como organizar imports

Seguir a PEP 8 significa que seu código vai parecer familiar para outros programadores Python, deixando a colaboração mais tranquila. Vamos cobrir as diretrizes essenciais da PEP 8 nas próximas seções.

Sim

Não

Escrever Código

Seguir Guia de Estilo?

Código Consistente e Legível

Código Inconsistente

Fácil de Entender

Fácil de Manter

Fácil de Colaborar

Carga Mental

Confusão

Bugs

40.2) Convenções de Nomenclatura: Variáveis, Funções e Classes (PEP 8)

40.2.1) Princípios Gerais de Nomenclatura

Bons nomes são descritivos e não ambíguos. Eles devem dizer o que algo representa ou faz sem exigir que você leia a implementação.

Princípios-chave:

  • Use palavras completas, não abreviações (exceto as muito comuns como id, url, html)
  • Seja específico: user_count é melhor que count, calculate_total_price é melhor que calculate
  • Evite nomes de uma letra, exceto em loops muito curtos ou fórmulas matemáticas
  • Não inclua informação de tipo nos nomes (Python é dinamicamente tipado)
python
# Nomes ruins - não fica claro o que representam
# AVISO: Estilo ruim - apenas para demonstração
# O que é 'n'? Um número? Um nome? Um nó?
# O que é 'd'? Uma data? Uma distância? Uma duração?
# O que é 'l'? Parece o número 1!
n = "Alice"
d = 25
l = [1, 2, 3]
calc = lambda x: x * 2
 
# Bons nomes - claros e descritivos
student_name = "Alice"
age_in_years = 25
test_scores = [1, 2, 3]
double_value = lambda x: x * 2

Exceção: variáveis curtas em loops

python
# Aceitável: bem curto, contexto claro
for i in range(10):
    print(i)
 
for x, y in coordinates:
    distance = (x**2 + y**2) ** 0.5
 
# Mas prefira nomes descritivos para clareza
for student_index in range(len(students)):
    print(students[student_index])
 
for point_x, point_y in coordinates:
    distance = (point_x**2 + point_y**2) ** 0.5

40.2.2) Nomes de Variáveis e Funções: snake_case

Em Python, variáveis e funções usam snake_case: tudo em minúsculas com palavras separadas por underscores.

python
# Variáveis
user_name = "Bob"
total_price = 99.99
is_valid = True
max_retry_count = 3
 
# Funções
def calculate_tax(amount, rate):
    """Calculate tax on a given amount."""
    return amount * rate
 
def send_email_notification(recipient, message):
    """Send an email to the specified recipient."""
    print(f"Sending to {recipient}: {message}")
 
# Usando as funções
tax_amount = calculate_tax(100, 0.08)
send_email_notification("user@example.com", "Welcome!")

Por que snake_case? É altamente legível. Os underscores criam limites claros entre palavras, deixando os nomes fáceis de “varrer” com os olhos. Compare calculatetotalprice (difícil de ler) com calculate_total_price (imediatamente claro).

40.2.3) Nomes de Constantes: UPPER_SNAKE_CASE

Constantes — valores que não deveriam mudar durante a execução do programa — usam UPPER_SNAKE_CASE: tudo em maiúsculas com underscores.

python
# Constantes no nível do módulo
MAX_LOGIN_ATTEMPTS = 3
DEFAULT_TIMEOUT_SECONDS = 30
PI = 3.14159
DATABASE_URL = "postgresql://localhost/mydb"
 
def validate_password_length(password):
    """Check if password meets minimum length requirement."""
    MIN_PASSWORD_LENGTH = 8  # Constante dentro da função
    return len(password) >= MIN_PASSWORD_LENGTH
 
# Usando constantes
if login_attempts > MAX_LOGIN_ATTEMPTS:
    print("Account locked")

Importante: Python não tem sintaxe embutida para constantes. Diferente de algumas linguagens (como const em JavaScript ou final em Java), Python não tem um jeito de declarar que uma variável não pode ser alterada.

Em vez disso, programadores Python usam uma convenção de nomenclatura para sinalizar intenção:

  • UPPER_SNAKE_CASE significa: "Eu pretendo que isso seja uma constante — não modifique"
  • Isso é uma ferramenta de comunicação entre programadores, não um recurso da linguagem
python
# Python não tem sintaxe de constante - isto é apenas uma variável normal
MAX_LOGIN_ATTEMPTS = 3
 
# Python não vai impedir você de modificá-la
MAX_LOGIN_ATTEMPTS = 5  # ❌ Tecnicamente funciona, mas viola a convenção
 
# A convenção de nomenclatura é um sinal de INTENÇÃO:
# "Eu nomeei isto em MAIÚSCULAS para mostrar que não quero que seja alterado"

Boa prática: se um valor realmente precisa mudar durante a execução do programa, não o nomeie como uma constante:

python
# Este valor vai mudar - use minúsculas
max_login_attempts = 3
max_login_attempts = 5  # ✅ OK - o nome indica que pode mudar
 
# Este valor nunca deveria mudar - use MAIÚSCULAS
MAX_LOGIN_ATTEMPTS = 3
# Não reatribua depois no código

A convenção ajuda programadores a entenderem sua intenção e evitarem bugs. Quando você vê MAX_LOGIN_ATTEMPTS, você sabe que não deve modificar isso.

40.2.4) Nomes de Classes: PascalCase

Nomes de classes usam PascalCase (também chamado CapWords): cada palavra começa com letra maiúscula, sem underscores.

python
# Definições de classes
class Student:
    """Represent a student with name and grades."""
    def __init__(self, name):
        self.name = name
        self.grades = []
 
class ShoppingCart:
    """Manage items in a shopping cart."""
    def __init__(self):
        self.items = []
    
    def add_item(self, item):
        """Add an item to the cart."""
        self.items.append(item)
 
class DatabaseConnection:
    """Handle database connection and queries."""
    def __init__(self, url):
        self.url = url
 
# Criando instâncias (observação: instâncias usam nomes de variável em snake_case)
student = Student("Alice")
shopping_cart = ShoppingCart()
db_connection = DatabaseConnection("localhost")

Por que PascalCase para classes? Isso distingue visualmente classes de funções e variáveis. Quando você vê Student(), você sabe imediatamente que está criando uma instância de uma classe. Quando você vê calculate_average(), você sabe que está chamando uma função.

40.2.5) Nomes Privados e Internos: Underscore no Início

Nomes que começam com um único underscore (_name) indicam uso interno — eles são feitos para uso dentro do módulo ou da classe, não por código externo.

Python não tem sintaxe para marcar métodos ou atributos como "privados" (diferente de private em Java ou C++). Em vez disso, Python usa uma convenção de nomenclatura com um underscore no início (_name) para comunicar intenção.

O que _name significa:

  • "Isso é para uso interno apenas"
  • "Eu fiz isso para usar dentro desta classe/módulo, não para código externo"
  • "Isso pode mudar a qualquer momento em versões futuras — não dependa disso"
python
class BankAccount:
    """Represent a bank account with balance tracking."""
    
    def __init__(self, account_number, initial_balance):
        self.account_number = account_number
        self._balance = initial_balance  # Atributo interno
    
    def deposit(self, amount):
        """Add money to the account."""
        if self._validate_amount(amount):  # Método interno
            self._balance += amount
    
    def _validate_amount(self, amount):
        """Internal helper to validate transaction amounts."""
        return amount > 0
    
    def get_balance(self):
        """Return the current balance."""
        return self._balance
 
# Usando a classe
account = BankAccount("12345", 1000)
account.deposit(500)
print(account.get_balance())  # Output: 1500
 
# Tecnicamente funciona, mas viola a convenção
print(account._balance)
# Output: 1500 (works, but you shouldn't do this!)
 
# Tecnicamente funciona, mas viola a convenção
result = account._validate_amount(100)
# Output: True (works, but you shouldn't do this!)

Ponto-chave: Python não consegue impedir você de acessar _balance ou chamar _validate_amount(). O underscore é um sinal entre programadores, não um recurso de segurança.

Por que Essa Convenção Existe

Como Python não consegue impor privacidade, o underscore é como autores de classes comunicam sua intenção:

O que o underscore sinaliza:

  • "Isto é implementação interna — pode mudar em versões futuras"
  • "Use os métodos públicos — eles têm garantia de permanecer estáveis"
  • "Se você depender de detalhes internos, seu código pode quebrar quando eu atualizar a biblioteca"

A convenção cria um contrato: autores de classes podem mudar livremente a implementação interna (qualquer coisa com _), mas precisam manter a interface pública estável. Isso permite que bibliotecas evoluam sem quebrar o código de quem usa.

40.2.6) Nomes Especiais: Underscores Duplos

Nomes com underscores duplos no começo e no fim (__name__) são métodos especiais ou métodos mágicos definidos pelo Python. Não crie seus próprios nomes com esse padrão — ele é reservado para uso do Python.

python
class Point:
    """Represent a 2D point."""
    
    def __init__(self, x, y):  # Método especial: inicialização
        self.x = x
        self.y = y
    
    def __str__(self):  # Método especial: representação em string
        return f"Point({self.x}, {self.y})"
    
    def __add__(self, other):  # Método especial: operador de adição
        return Point(self.x + other.x, self.y + other.y)
 
p1 = Point(1, 2)
p2 = Point(3, 4)
print(p1)  # Output: Point(1, 2)
print(p1 + p2)  # Output: Point(4, 6)

Como aprendemos no Capítulo 31, esses métodos especiais permitem sobrecarga de operadores e integração com as funções embutidas do Python.

40.2.7) Tabela-Resumo de Nomenclatura

TipoConvençãoExemplo
Variáveissnake_caseuser_name, total_count
Funçõessnake_casecalculate_tax(), send_email()
ConstantesUPPER_SNAKE_CASEMAX_SIZE, DEFAULT_TIMEOUT
ClassesPascalCaseStudent, ShoppingCart
Interno/Privado_leading_underscore_balance, _validate()
Especial/Mágicodouble_underscore__init__, __str__

40.3) Layout de Código: Indentação, Espaçamento e Linhas em Branco

40.3.1) Indentação: Quatro Espaços

Python usa indentação para definir blocos de código. Sempre use 4 espaços por nível de indentação — nunca tabs, e nunca misture tabs e espaços.

python
def calculate_grade(score):
    """Determine letter grade from numeric score."""
    if score >= 90:
        return "A"
    elif score >= 80:
        return "B"
    elif score >= 70:
        return "C"
    else:
        return "F"
 
# Indentação aninhada: 4 espaços por nível
def process_students(students):
    """Process a list of student records."""
    for student in students:
        if student["active"]:
            grade = calculate_grade(student["score"])
            print(f"{student['name']}: {grade}")
 
students = [
    {"name": "Alice", "score": 92, "active": True},
    {"name": "Bob", "score": 78, "active": True}
]
process_students(students)
# Output:
# Alice: A
# Bob: C

Por que 4 espaços? É o padrão da comunidade Python. A maior parte do código Python que você encontrar usa 4 espaços, então seguir essa convenção torna seu código consistente com o ecossistema.

Configurando seu editor: editores modernos podem ser configurados para inserir 4 espaços quando você aperta Tab. Isso dá a conveniência da tecla Tab enquanto mantém o padrão de 4 espaços.

40.3.2) Comprimento Máximo de Linha: 79 Caracteres

A PEP 8 recomenda limitar linhas a 79 caracteres (com até 99 caracteres para docstrings e comentários). Isso pode parecer restritivo, mas tem benefícios práticos:

  • O código continua legível em telas menores
  • Você pode ver dois arquivos lado a lado
  • Isso incentiva quebrar expressões complexas em partes mais simples

Observação: muitos projetos modernos usam limites um pouco maiores (88, 100 ou 120 caracteres). O ponto principal é a consistência dentro do seu projeto. Escolha um limite e mantenha-o.

python
# Muito longo - difícil de ler
# AVISO: Estilo ruim - apenas para demonstração
def calculate_monthly_payment(principal, annual_rate, years):
    return principal * (annual_rate / 12) * (1 + annual_rate / 12) ** (years * 12) / ((1 + annual_rate / 12) ** (years * 12) - 1)
 
# Melhor - quebrado em linhas legíveis
def calculate_monthly_payment(principal, annual_rate, years):
    """Calculate monthly loan payment using amortization formula."""
    monthly_rate = annual_rate / 12
    num_payments = years * 12
    
    numerator = principal * monthly_rate * (1 + monthly_rate) ** num_payments
    denominator = (1 + monthly_rate) ** num_payments - 1
    
    return numerator / denominator
 
payment = calculate_monthly_payment(200000, 0.045, 30)
print(f"Monthly payment: ${payment:.2f}")  # Output: Monthly payment: $1013.37

Quebrando linhas longas: quando você precisar quebrar uma linha, use continuação implícita dentro de parênteses, colchetes ou chaves:

python
# Chamada de função longa
result = some_function(
    first_argument,
    second_argument,
    third_argument,
    fourth_argument
)
 
# Lista longa
colors = [
    "red", "green", "blue",
    "yellow", "orange", "purple",
    "pink", "brown", "gray"
]
 
# String longa
message = (
    "This is a very long message that needs to be broken "
    "across multiple lines for readability. Python automatically "
    "concatenates adjacent string literals."
)
print(message)
# Output: This is a very long message that needs to be broken across multiple lines for readability. Python automatically concatenates adjacent string literals.

40.3.3) Espaçamento ao Redor de Operadores e Depois de Vírgulas

Use espaços ao redor de operadores e depois de vírgulas para melhorar a legibilidade:

python
# Espaçamento ruim - apertado e difícil de ler
# AVISO: Estilo ruim - apenas para demonstração
x=5
y=x*2+3
result=calculate_tax(100,0.08)
data=[1,2,3,4,5]
 
# Bom espaçamento - claro e legível
x = 5
y = x * 2 + 3
result = calculate_tax(100, 0.08)
data = [1, 2, 3, 4, 5]
 
# Espaçamento em expressões
total = (price * quantity) + shipping_cost
is_valid = (age >= 18) and (has_license == True)
 
# Espaçamento em definições de função
def calculate_discount(price, discount_rate, minimum_purchase=0):
    """Calculate discounted price if minimum purchase is met."""
    if price >= minimum_purchase:
        return price * (1 - discount_rate)
    return price

Exceção: não use espaços ao redor de = em argumentos nomeados (keyword arguments) ou em valores padrão de parâmetros:

python
# Espaçamento correto para keyword arguments
result = calculate_discount(price=100, discount_rate=0.1, minimum_purchase=50)
 
# Espaçamento correto para parâmetros padrão
def greet(name, greeting="Hello"):
    return f"{greeting}, {name}!"

40.3.4) Linhas em Branco para Separação Lógica

Use linhas em branco para separar seções lógicas do código:

Duas linhas em branco entre funções e classes de nível superior:

python
def first_function():
    """First function."""
    pass
 
 
def second_function():
    """Second function."""
    pass
 
 
class MyClass:
    """A class definition."""
    pass

Uma linha em branco entre métodos dentro de uma classe:

python
class Student:
    """Represent a student with grades."""
    
    def __init__(self, name):
        self.name = name
        self.grades = []
    
    def add_grade(self, grade):
        """Add a grade to the student's record."""
        self.grades.append(grade)
    
    def get_average(self):
        """Calculate the student's grade average."""
        if not self.grades:
            return 0
        return sum(self.grades) / len(self.grades)

Linhas em branco dentro de funções para separar etapas lógicas:

python
def process_order(order_items, customer):
    """Process a customer order and calculate total."""
    
    # Calcular subtotal
    subtotal = 0
    for item in order_items:
        subtotal += item["price"] * item["quantity"]
    
    # Aplicar desconto do cliente
    discount = 0
    if customer["is_premium"]:
        discount = subtotal * 0.1
    
    # Calcular imposto
    tax = (subtotal - discount) * 0.08
    
    # Calcular total final
    total = subtotal - discount + tax
    
    return {
        "subtotal": subtotal,
        "discount": discount,
        "tax": tax,
        "total": total
    }

Essas linhas em branco funcionam como “parágrafos” visuais, deixando a estrutura do código imediatamente evidente.

40.3.5) Evitando Espaços em Branco no Fim da Linha

Não deixe espaços no final das linhas — eles são invisíveis, mas podem causar problemas com sistemas de controle de versão e alguns editores.

python
# Ruim - espaços invisíveis no fim da linha (mostrados como · para ilustração)
# AVISO: Estilo ruim - apenas para demonstração
def calculate(x):···
    return x * 2···
 
# Bom - sem espaços no fim da linha
def calculate(x):
    return x * 2

A maioria dos editores modernos pode ser configurada para remover automaticamente os espaços em branco no fim da linha quando você salva um arquivo.

40.4) Documentação: Escrevendo Comentários e Docstrings Úteis

40.4.1) Quando Escrever Comentários

Comentários explicam por que o código faz algo, não o que ele faz. Variáveis e funções bem nomeadas deveriam tornar o “o que” óbvio.

python
# Comentário ruim - diz o óbvio
# AVISO: Estilo ruim - apenas para demonstração
x = x + 1  # Adicionar 1 a x
 
# Bom comentário - explica o porquê
x = x + 1  # Ajustar para indexação baseada em zero
 
# Comentário ruim - redundante com o código
# AVISO: Estilo ruim - apenas para demonstração
# Verificar se a idade é maior ou igual a 18
if age >= 18:
    print("Adult")
 
# Bom comentário - explica a lógica de negócio
# Idade legal para beber nos EUA
if age >= 21:
    print("Can purchase alcohol")

Quando comentários são valiosos:

  1. Explicando algoritmos complexos:
python
def binary_search(sorted_list, target):
    """Search for target in sorted list using binary search."""
    left = 0
    right = len(sorted_list) - 1
    
    while left <= right:
        # Calcular o ponto do meio, evitando overflow de inteiros
        # (right + left) // 2 poderia estourar com índices muito grandes
        mid = left + (right - left) // 2
        
        if sorted_list[mid] == target:
            return mid
        elif sorted_list[mid] < target:
            left = mid + 1  # O target está na metade direita
        else:
            right = mid - 1  # O target está na metade esquerda
    
    return -1  # Target não encontrado
  1. Esclarecendo regras de negócio não óbvias:
python
def calculate_shipping_cost(weight, distance):
    """Calculate shipping cost based on weight and distance."""
    base_cost = 5.00
    
    # Promoção de frete grátis para itens pesados (política da empresa em 2024)
    # Isso incentiva pedidos em volume e reduz custos de frete por unidade
    if weight > 50:
        return 0
    
    # Tarifa padrão: $0.50 por libra mais $0.10 por milha
    # Baseado no contrato com a transportadora negociado no Q1 2024
    return base_cost + (weight * 0.50) + (distance * 0.10)
  1. Documentando contornos (workarounds) ou soluções temporárias:
python
def process_data(data):
    """Process incoming data records."""
    # TODO: Esta é uma correção temporária para registros malformados
    # Remover quando a validação de dados for implementada na origem
    if not isinstance(data, list):
        data = [data]
    
    for record in data:
        # Processar cada registro
        pass

40.4.2) Escrevendo Docstrings Eficazes

Docstrings são comentários especiais que documentam módulos, classes e funções. Elas ficam entre aspas triplas e aparecem como a primeira instrução na definição.

python
def calculate_bmi(weight_kg, height_m):
    """
    Calculate Body Mass Index (BMI).
    
    BMI is calculated as weight in kilograms divided by the square of height in meters.
    
    Args:
        weight_kg: Weight in kilograms (float or int)
        height_m: Height in meters (float or int)
    
    Returns:
        float: The calculated BMI value
    
    Example:
        >>> calculate_bmi(70, 1.75)
        22.857142857142858
    """
    return weight_kg / (height_m ** 2)
 
# Acessando docstrings
print(calculate_bmi.__doc__)
# Output:
#     Calculate Body Mass Index (BMI).
#     
#     BMI is calculated as weight in kilograms divided by the square of height in meters.
#     ...

Docstrings de uma linha para funções simples:

python
def square(x):
    """Return the square of x."""
    return x * x
 
def is_even(n):
    """Return True if n is even, False otherwise."""
    return n % 2 == 0

Docstrings de múltiplas linhas para funções complexas:

python
def find_prime_factors(n):
    """
    Find all prime factors of a positive integer.
    
    This function returns a list of prime numbers that, when multiplied
    together, equal the input number. The factors are returned in ascending order.
    
    Args:
        n: A positive integer greater than 1
    
    Returns:
        list: Prime factors in ascending order
    
    Raises:
        ValueError: If n is less than 2
    
    Example:
        >>> find_prime_factors(12)
        [2, 2, 3]
        >>> find_prime_factors(17)
        [17]
    """
    if n < 2:
        raise ValueError("n must be at least 2")
    
    factors = []
    divisor = 2
    
    while n > 1:
        while n % divisor == 0:
            factors.append(divisor)
            n = n // divisor
        divisor += 1
    
    return factors

Docstrings de classe:

python
class BankAccount:
    """
    Represent a bank account with deposit and withdrawal operations.
    
    This class maintains an account balance and provides methods for
    depositing and withdrawing money. All transactions are validated to prevent negative balances.
    
    Attributes:
        account_number: Unique identifier for the account
        balance: Current account balance in dollars
    """
    
    def __init__(self, account_number, initial_balance=0):
        """
        Initialize a new bank account.
        
        Args:
            account_number: Unique account identifier (string)
            initial_balance: Starting balance (default: 0)
        """
        self.account_number = account_number
        self.balance = initial_balance
    
    def deposit(self, amount):
        """
        Add money to the account.
        
        Args:
            amount: Amount to deposit (must be positive)
        
        Raises:
            ValueError: If amount is not positive
        """
        if amount <= 0:
            raise ValueError("Deposit amount must be positive")
        self.balance += amount

40.4.3) Convenções de Docstrings

Primeira linha: resumo breve do que a função/classe faz. Deve caber em uma linha.

Linha em branco: separa o resumo da descrição detalhada.

Descrição detalhada: explica o que a função faz, quaisquer detalhes importantes e como usar.

Args/Parameters: lista cada parâmetro com seu tipo e finalidade.

Returns: descreve o que a função retorna e seu tipo.

Raises: documenta quaisquer exceções que a função pode levantar.

Example: mostra um uso típico (opcional, mas útil).

python
def calculate_compound_interest(principal, rate, time, compounds_per_year=1):
    """
    Calculate compound interest on an investment.
    
    Uses the compound interest formula: A = P(1 + r/n)^(nt)
    where A is the final amount, P is principal, r is annual rate,
    n is compounds per year, and t is time in years.
    
    Args:
        principal: Initial investment amount (float)
        rate: Annual interest rate as decimal (e.g., 0.05 for 5%)
        time: Investment period in years (float)
        compounds_per_year: Number of times interest compounds annually
                           (default: 1 for annual compounding)
    
    Returns:
        float: Final amount after compound interest
    
    Example:
        >>> calculate_compound_interest(1000, 0.05, 10, 12)
        1647.0095
    """
    return principal * (1 + rate / compounds_per_year) ** (compounds_per_year * time)

40.4.4) Comentários TODO para Trabalho Futuro

Use comentários TODO para marcar áreas que precisam de atenção no futuro:

python
def process_payment(amount, payment_method):
    """Process a payment transaction."""
    # TODO: Adicionar suporte a pagamentos com criptomoedas
    # TODO: Implementar verificações de detecção de fraude
    
    if payment_method == "credit_card":
        return process_credit_card(amount)
    elif payment_method == "paypal":
        return process_paypal(amount)
    else:
        raise ValueError(f"Unsupported payment method: {payment_method}")

Muitos editores conseguem buscar comentários TODO, o que facilita encontrar áreas que precisam de trabalho.

40.5) Organizando Seu Código: Imports, Constantes, Funções e Main

40.5.1) Estrutura Padrão de Módulo

Um módulo Python bem organizado segue esta estrutura:

  1. Docstring do módulo: descreve o que o módulo faz
  2. Imports: biblioteca padrão, terceiros e depois imports locais
  3. Constantes: constantes no nível do módulo
  4. Funções e classes: código principal
  5. Bloco principal de execução: código que roda quando o script é executado
python
"""
student_manager.py
 
Manage student records including grades and GPA calculations.
 
This module provides functions for adding students, recording grades,
and calculating grade point averages.
"""
 
# Imports da biblioteca padrão
import sys
from datetime import datetime
 
# Imports de terceiros (se houver)
# import requests
 
# Imports locais (se houver)
# from .database import save_student
 
# Constantes
MAX_GRADE = 100
MIN_GRADE = 0
PASSING_GRADE = 60
 
# Funções
def calculate_gpa(grades):
    """
    Calculate GPA from a list of numeric grades.
    
    Args:
        grades: List of numeric grades (0-100)
    
    Returns:
        float: GPA on 4.0 scale
    """
    if not grades:
        return 0.0
    
    average = sum(grades) / len(grades)
    
    # Converter para a escala 4.0
    if average >= 90:
        return 4.0
    elif average >= 80:
        return 3.0
    elif average >= 70:
        return 2.0
    elif average >= 60:
        return 1.0
    else:
        return 0.0
 
def validate_grade(grade):
    """
    Check if a grade is within valid range.
    
    Args:
        grade: Numeric grade to validate
    
    Returns:
        bool: True if grade is valid, False otherwise
    """
    return MIN_GRADE <= grade <= MAX_GRADE
 
# Execução principal
if __name__ == "__main__":
    # Código que roda quando o script é executado diretamente
    test_grades = [85, 92, 78, 88]
    gpa = calculate_gpa(test_grades)
    print(f"GPA: {gpa}")  # Output: GPA: 3.0

40.5.2) Organização de Imports

Agrupe imports em três seções, separadas por linhas em branco:

  1. Imports da biblioteca padrão: módulos embutidos do Python
  2. Imports de terceiros: pacotes instalados (como requests, numpy)
  3. Imports locais: seus próprios módulos
python
# Imports da biblioteca padrão
import os
import sys
from datetime import datetime, timedelta
from pathlib import Path
 
# Imports de terceiros
import requests
from flask import Flask, render_template
 
# Imports locais da aplicação
from myapp.database import connect_db
from myapp.models import User, Product
from myapp.utils import format_currency

Estilos de import:

python
# Importar o módulo inteiro
import math
result = math.sqrt(16)  # Output: 4.0
 
# Importar itens específicos
from math import sqrt, pi
result = sqrt(16)  # Output: 4.0
 
# Importar com alias
import numpy as np
array = np.array([1, 2, 3])
 
# Importar múltiplos itens
from os import path, getcwd, listdir

Evite imports coringa (from module import *) — eles deixam pouco claro de onde os nomes vêm:

python
# Ruim - não fica claro de onde sqrt vem
# AVISO: Estilo ruim - apenas para demonstração
from math import *
result = sqrt(16)
 
# Bom - import explícito
from math import sqrt
result = sqrt(16)

40.5.3) Organizando Constantes

Coloque constantes no nível do módulo perto do topo, depois dos imports:

python
"""Configuration settings for the application."""
 
import os
 
# Constantes da aplicação
APP_NAME = "Student Manager"
VERSION = "1.0.0"
DEBUG_MODE = True
 
# Configuração do banco de dados
DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///students.db")
MAX_CONNECTIONS = 10
 
# Regras de negócio
MAX_STUDENTS_PER_CLASS = 30
PASSING_GRADE = 60
GRADE_WEIGHTS = {
    "homework": 0.3,
    "midterm": 0.3,
    "final": 0.4
}
 
def calculate_final_grade(homework, midterm, final):
    """Calculate weighted final grade."""
    return (
        homework * GRADE_WEIGHTS["homework"] +
        midterm * GRADE_WEIGHTS["midterm"] +
        final * GRADE_WEIGHTS["final"]
    )

40.5.4) Ordenação Lógica de Funções

Organize funções em uma ordem lógica:

  1. Funções públicas primeiro: funções que devem ser usadas por outros módulos
  2. Funções auxiliares depois: funções internas que dão suporte às públicas
  3. Funções relacionadas juntas: agrupe funções que trabalham em conjunto
python
"""Order processing module."""
 
# Funções da API pública
def process_order(order_items, customer):
    """
    Process a customer order.
    
    This is the main entry point for order processing.
    """
    subtotal = _calculate_subtotal(order_items)
    discount = _calculate_discount(subtotal, customer)
    tax = _calculate_tax(subtotal - discount)
    total = subtotal - discount + tax
    
    return {
        "subtotal": subtotal,
        "discount": discount,
        "tax": tax,
        "total": total
    }
 
def validate_order(order_items):
    """Validate that an order contains valid items."""
    if not order_items:
        return False
    
    for item in order_items:
        if not _validate_item(item):
            return False
    
    return True
 
# Funções auxiliares internas
def _calculate_subtotal(items):
    """Calculate order subtotal (internal use)."""
    total = 0
    for item in items:
        total += item["price"] * item["quantity"]
    return total
 
def _calculate_discount(subtotal, customer):
    """Calculate customer discount (internal use)."""
    if customer.get("is_premium"):
        return subtotal * 0.1
    return 0
 
def _calculate_tax(amount):
    """Calculate sales tax (internal use)."""
    TAX_RATE = 0.08
    return amount * TAX_RATE
 
def _validate_item(item):
    """Validate a single order item (internal use)."""
    required_fields = ["name", "price", "quantity"]
    return all(field in item for field in required_fields)

Observe como as funções públicas (process_order, validate_order) vêm primeiro, e as funções auxiliares (prefixadas com _) vêm depois. Isso deixa claro quais funções são a API principal.

40.5.5) Organização de Classes Dentro de Módulos

Quando um módulo contém classes, organize-as de forma lógica:

python
"""User management system."""
 
# Constantes
DEFAULT_ROLE = "user"
ADMIN_ROLE = "admin"
 
# Classes base primeiro
class User:
    """Base user class."""
    
    def __init__(self, username, email):
        self.username = username
        self.email = email
        self.role = DEFAULT_ROLE
    
    def can_edit(self, resource):
        """Check if user can edit a resource."""
        return resource.owner == self.username
 
# Classes derivadas depois das classes base
class AdminUser(User):
    """Administrator with elevated privileges."""
    
    def __init__(self, username, email):
        super().__init__(username, email)
        self.role = ADMIN_ROLE
    
    def can_edit(self, resource):
        """Admins can edit any resource."""
        return True
 
# Classes relacionadas agrupadas
class Resource:
    """Represent a resource that can be owned and edited."""
    
    def __init__(self, name, owner):
        self.name = name
        self.owner = owner
 
# Funções utilitárias relacionadas às classes
def create_user(username, email, is_admin=False):
    """Factory function to create appropriate user type."""
    if is_admin:
        return AdminUser(username, email)
    return User(username, email)

Princípios de organização de classes:

  • Classes base antes de classes derivadas (leitores precisam entender a base primeiro)
  • Classes relacionadas agrupadas (User e Resource são relacionadas)
  • Funções utilitárias que trabalham com classes vêm depois das definições de classe
  • Cada classe deve ter uma docstring clara explicando seu propósito

40.6) O Padrão if __name__ == "__main__"

40.6.1) Entendendo o Padrão

Todo arquivo Python tem uma variável embutida chamada __name__. O Python define automaticamente o valor dessa variável dependendo de como o arquivo está sendo usado:

  • Quando você executa um arquivo diretamente (por exemplo, python my_script.py), o Python define __name__ como "__main__"
  • Quando você importa um arquivo como um módulo, o Python define __name__ como o nome do módulo (o nome do arquivo sem .py)

Isso permite que você escreva código que só roda quando o arquivo é executado diretamente, e não quando é importado:

python
"""math_utils.py - Mathematical utility functions."""
 
def add(a, b):
    """Add two numbers."""
    return a + b
 
def multiply(a, b):
    """Multiply two numbers."""
    return a * b
 
# Este código só roda quando o arquivo é executado diretamente
if __name__ == "__main__":
    # Testar as funções
    print(f"5 + 3 = {add(5, 3)}")  # Output: 5 + 3 = 8
    print(f"5 * 3 = {multiply(5, 3)}")  # Output: 5 * 3 = 15

Quando você executa python math_utils.py, você verá a saída. Mas quando você o importa em outro arquivo:

python
# another_file.py
from math_utils import add, multiply
 
result = add(10, 20)
print(result)  # Output: 30
# O código de teste de math_utils.py NÃO roda

Perceba que o código de teste (dentro de if __name__ == "__main__":) NÃO roda quando é importado!

python math_utils.py

import math_utils

Arquivo Python Executado

Como ele é executado?

name = 'main'

name = 'math_utils'

Código em if name == 'main' roda

Código em if name == 'main' é ignorado

40.6.2) Por que Esse Padrão Importa

Esse padrão serve a vários propósitos importantes:

1. Testes e demonstração: você pode incluir exemplos de uso no mesmo arquivo das suas funções:

python
"""temperature.py - Temperature conversion utilities."""
 
def celsius_to_fahrenheit(celsius):
    """Convert Celsius to Fahrenheit."""
    return (celsius * 9/5) + 32
 
def fahrenheit_to_celsius(fahrenheit):
    """Convert Fahrenheit to Celsius."""
    return (fahrenheit - 32) * 5/9
 
if __name__ == "__main__":
    # Demonstrar as funções
    print("Exemplos de Conversão de Temperatura:")
    print(f"0°C = {celsius_to_fahrenheit(0)}°F")  # Output: 0°C = 32.0°F
    print(f"100°C = {celsius_to_fahrenheit(100)}°F")  # Output: 100°C = 212.0°F
    print(f"32°F = {fahrenheit_to_celsius(32)}°C")  # Output: 32°F = 0.0°C

2. Módulos reutilizáveis: o mesmo arquivo pode ser tanto um script independente quanto um módulo importável:

python
"""data_processor.py - Process and analyze data files."""
 
import sys
 
def load_data(filename):
    """Load data from a file."""
    with open(filename) as f:
        return [line.strip() for line in f]
 
def analyze_data(data):
    """Perform analysis on data."""
    return {
        "count": len(data),
        "average_length": sum(len(item) for item in data) / len(data)
    }
 
if __name__ == "__main__":
    # Quando executado como script, processar argumentos da linha de comando
    if len(sys.argv) < 2:
        print("Usage: python data_processor.py <filename>")
        sys.exit(1)
    
    filename = sys.argv[1]
    data = load_data(filename)
    results = analyze_data(data)
    
    print(f"Processed {results['count']} items")
    print(f"Average length: {results['average_length']:.2f}")

Você pode executar isso como um script:

bash
$ python data_processor.py data.txt
Processed 42 items
Average length: 15.23

Ou importar em outro arquivo:

python
# my_analysis.py
from data_processor import load_data, analyze_data
 
my_data = load_data("myfile.txt")
results = analyze_data(my_data)
print(f"Found {results['count']} items")

40.6.3) Padrões Comuns para Blocos Main

Padrão 1: Casos de teste simples

python
"""calculator.py - Basic calculator operations."""
 
def add(a, b):
    """Add two numbers."""
    return a + b
 
def subtract(a, b):
    """Subtract b from a."""
    return a - b
 
if __name__ == "__main__":
    # Testes rápidos
    assert add(2, 3) == 5
    assert subtract(10, 4) == 6
    print("All tests passed!")  # Output: All tests passed!

Padrão 2: Função main

Para scripts mais complexos, defina uma função main():

python
"""report_generator.py - Generate reports from data."""
 
import sys
 
def load_data(filename):
    """Load data from file."""
    # Implementation here
    pass
 
def generate_report(data):
    """Generate report from data."""
    # Implementation here
    pass
 
def save_report(report, output_file):
    """Save report to file."""
    # Implementation here
    pass
 
def main():
    """Main entry point for the script."""
    if len(sys.argv) < 3:
        print("Usage: python report_generator.py <input> <output>")
        return 1
    
    input_file = sys.argv[1]
    output_file = sys.argv[2]
    
    try:
        data = load_data(input_file)
        report = generate_report(data)
        save_report(report, output_file)
        print(f"Report saved to {output_file}")
        return 0
    except Exception as e:
        print(f"Error: {e}")
        return 1
 
if __name__ == "__main__":
    # Sair com o código de status retornado por main (0 = sucesso, 1 = erro)
    sys.exit(main())

Esse padrão tem várias vantagens:

  • A função main() pode ser testada de forma independente
  • Ponto de entrada claro para o script
  • Códigos de saída adequados (0 para sucesso, diferente de zero para erros)
  • Separação limpa entre a lógica do script e as funções do módulo

40.6.4) Boas Práticas para Blocos Main

Mantenha blocos main focados: o código dentro de if __name__ == "__main__" deveria principalmente lidar com a execução do script, e não conter lógica complexa:

python
# Ruim - lógica complexa no bloco main
# AVISO: Estilo ruim - apenas para demonstração
if __name__ == "__main__":
    data = []
    for i in range(100):
        if i % 2 == 0:
            data.append(i * 2)
    result = sum(data) / len(data)
    print(result)
 
# Bom - lógica em funções, bloco main coordena
def generate_even_doubles(limit):
    """Generate doubled even numbers up to limit."""
    return [i * 2 for i in range(limit) if i % 2 == 0]
 
def calculate_average(numbers):
    """Calculate average of numbers."""
    return sum(numbers) / len(numbers)
 
if __name__ == "__main__":
    data = generate_even_doubles(100)
    result = calculate_average(data)
    print(result)  # Output: 99.0

Use uma função main() para scripts complexos: como mostrado antes, definir uma função main() torna seu script mais testável e organizado.

Documente o uso do script: se seu script aceita argumentos de linha de comando, documente-os na docstring do módulo:

python
"""
file_processor.py - Process text files with various operations.
 
Usage:
    python file_processor.py <input_file> <output_file> [--uppercase]
 
Arguments:
    input_file: Path to input file
    output_file: Path to output file
    --uppercase: Convert text to uppercase (optional)
"""
 
import sys
 
def process_file(input_path, output_path, uppercase=False):
    """Process file with specified options."""
    with open(input_path) as f:
        content = f.read()
    
    if uppercase:
        content = content.upper()
    
    with open(output_path, 'w') as f:
        f.write(content)
 
if __name__ == "__main__":
    if len(sys.argv) < 3:
        print(__doc__)  # Imprimir a docstring do módulo
        sys.exit(1)
    
    input_file = sys.argv[1]
    output_file = sys.argv[2]
    uppercase = "--uppercase" in sys.argv
    
    process_file(input_file, output_file, uppercase)
    print(f"Processed {input_file} -> {output_file}")

Escrever código limpo e legível é uma habilidade que se desenvolve com prática. As convenções e padrões neste capítulo não são regras arbitrárias — são práticas comprovadas que tornam o código mais fácil de entender, manter e depurar. À medida que você escreve mais código Python, esses padrões vão se tornar naturais.

Lembre-se: código é lido muito mais vezes do que é escrito. Os poucos segundos extras que você gasta escolhendo um nome claro, adicionando um comentário útil ou organizando seus imports corretamente vão economizar horas de confusão depois — para você e para outras pessoas que trabalharem com o seu código.

No próximo capítulo, vamos explorar técnicas de debugging e testes que se baseiam nessas práticas de código limpo, ajudando você a escrever não apenas código legível, mas código correto e confiável.

© 2025. Primesoft Co., Ltd.
support@primesoft.ai