Voltar ao Glossario
Glossario Python

Módulo Python: O que É e Como Funciona | Python Brasil

Guia completo sobre módulos Python: importações, sys.path, __all__, importações circulares, importlib, lazy loading e arquivos .pyc.

O que é um Módulo?

Um módulo em Python é simplesmente um arquivo .py que contém código Python — funções, classes, variáveis e instruções executáveis. Módulos são a unidade básica de organização e reutilização de código na linguagem.

Ao importar um módulo, o Python executa o arquivo inteiro uma vez e armazena o resultado em memória, disponibilizando seu conteúdo com um namespace específico. A biblioteca padrão do Python inclui mais de 200 módulos prontos, o que originou a frase “Python vem com baterias incluídas”.

Como importar módulos

# Importar o módulo completo — acesso via namespace
import math
print(math.sqrt(16))  # 4.0
print(math.pi)        # 3.141592653589793

# Importar nomes específicos — acesso direto
from datetime import datetime, timedelta
agora = datetime.now()
amanha = agora + timedelta(days=1)

# Importar com alias — convenção para bibliotecas comuns
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# Importar tudo (evite em produção — polui o namespace)
from math import *
print(sqrt(25))  # 5.0 — mas qual sqrt é essa? math.sqrt? numpy.sqrt?

# Verificar o que está no namespace atual
print(dir())

A forma preferida é import modulo ou from modulo import nome_especifico. O from modulo import * raramente é adequado, pois torna difícil saber a origem de cada nome.

O caminho de busca: sys.path

Quando você escreve import requests, o Python precisa encontrar o arquivo correspondente. Ele percorre uma lista de diretórios na ordem definida em sys.path:

import sys

# Ver o caminho de busca atual
for caminho in sys.path:
    print(caminho)

# Saída típica:
# ''                                         # diretório atual
# /usr/lib/python312.zip
# /usr/lib/python3.12
# /usr/lib/python3.12/lib-dynload
# /home/usuario/.local/lib/python3.12/site-packages  # pip --user
# /usr/local/lib/python3.12/dist-packages            # pip global

# Adicionar diretório temporariamente
sys.path.insert(0, "/caminho/para/meus/modulos")
import meu_modulo

# Adicionar permanentemente via variável de ambiente
# export PYTHONPATH="/caminho/para/meus/modulos:$PYTHONPATH"

A string vazia '' no início representa o diretório de trabalho atual, por isso um arquivo requests.py na pasta atual sobrescreveria a biblioteca requests instalada. Cuidado ao nomear seus arquivos com nomes de bibliotecas populares.

Criando seus próprios módulos

# utils.py — seu módulo de utilitários
"""
Módulo de utilitários para formatação de dados brasileiros.
"""

def formatar_cpf(cpf: str) -> str:
    """Formata um CPF: '12345678901' -> '123.456.789-01'"""
    cpf = "".join(filter(str.isdigit, cpf))
    if len(cpf) != 11:
        raise ValueError(f"CPF deve ter 11 dígitos, recebeu {len(cpf)}")
    return f"{cpf[:3]}.{cpf[3:6]}.{cpf[6:9]}-{cpf[9:]}"

def formatar_moeda(valor: float, simbolo: str = "R$") -> str:
    """Formata valor como moeda: 1500.5 -> 'R$ 1.500,50'"""
    return f"{simbolo} {valor:_.2f}".replace(".", ",").replace("_", ".")

VERSAO = "1.2.0"

# app.py — usando o módulo
from utils import formatar_cpf, formatar_moeda, VERSAO

print(formatar_cpf("12345678901"))  # 123.456.789-01
print(formatar_moeda(1500.50))      # R$ 1.500,50
print(f"Utils versão {VERSAO}")     # Utils versão 1.2.0

Controlando a API pública com all

A variável __all__ define quais nomes são exportados quando alguém usa from modulo import *. Também serve como documentação da API pública:

# meu_modulo.py

__all__ = ["funcao_publica", "ClassePublica"]  # API pública explícita

def funcao_publica():
    """Esta função é parte da API pública."""
    return _funcao_interna()

def _funcao_interna():
    """Convenção: underscore indica uso interno."""
    return "resultado interno"

class ClassePublica:
    pass

class _ClasseInterna:
    """Não exportada — uso interno do módulo."""
    pass

# from meu_modulo import * importa apenas funcao_publica e ClassePublica
# _funcao_interna e _ClasseInterna NÃO são importadas

O bloco if name == “main

# calculadora.py

def somar(a: float, b: float) -> float:
    return a + b

def subtrair(a: float, b: float) -> float:
    return a - b

# Este bloco SÓ executa quando o arquivo é rodado diretamente
# Quando importado como módulo, __name__ == "calculadora" (não "__main__")
if __name__ == "__main__":
    print(f"2 + 3 = {somar(2, 3)}")
    print(f"10 - 4 = {subtrair(10, 4)}")

# python calculadora.py  -> executa o bloco
# import calculadora     -> NÃO executa o bloco

Esse padrão é essencial para criar módulos que funcionam tanto como scripts independentes quanto como bibliotecas importáveis.

Importações relativas vs absolutas

Dentro de pacotes, você pode usar importações relativas (com ponto) ou absolutas:

# Estrutura do pacote:
# meu_app/
# ├── __init__.py
# ├── core.py
# └── utils/
#     ├── __init__.py
#     ├── formatacao.py
#     └── validacao.py

# Em meu_app/core.py — importações absolutas (sempre funcionam)
from meu_app.utils.formatacao import formatar_cpf
from meu_app.utils.validacao import validar_email

# Em meu_app/core.py — importações relativas (só dentro de pacotes)
from .utils.formatacao import formatar_cpf    # . = diretório atual
from .utils.validacao import validar_email
from ..outro_pacote import algo               # .. = diretório pai

Importações absolutas são mais explícitas e geralmente preferidas. Importações relativas são úteis em pacotes grandes para evitar repetir o nome do pacote raiz.

Importações circulares e como resolvê-las

Importações circulares ocorrem quando o módulo A importa o módulo B, e B importa A. O Python detecta ciclos e pode lançar ImportError:

# PROBLEMA — importação circular
# modulo_a.py
from modulo_b import funcao_b

def funcao_a():
    return funcao_b()

# modulo_b.py
from modulo_a import funcao_a  # ImportError!

def funcao_b():
    return funcao_a()

# SOLUÇÃO 1 — importar dentro da função (lazy import)
# modulo_b.py
def funcao_b():
    from modulo_a import funcao_a  # importado só quando necessário
    return funcao_a()

# SOLUÇÃO 2 — extrair o código compartilhado para um terceiro módulo
# modulo_comum.py
def logica_compartilhada():
    return "resultado"

# modulo_a.py e modulo_b.py importam de modulo_comum

# SOLUÇÃO 3 — reestruturar a arquitetura
# Geralmente importações circulares indicam problema de design

A melhor solução é sempre a reestruturação: se A e B dependem mutuamente, provavelmente há uma abstração faltando.

importlib: importações dinâmicas

O módulo importlib permite importar módulos de forma programática em tempo de execução:

import importlib

# Importar um módulo pelo nome como string
nome_modulo = "json"
modulo = importlib.import_module(nome_modulo)
print(modulo.dumps({"chave": "valor"}))

# Sistema de plugins dinâmico
PLUGINS_DISPONIVEIS = ["plugin_csv", "plugin_json", "plugin_xml"]

plugins = {}
for nome in PLUGINS_DISPONIVEIS:
    try:
        plugins[nome] = importlib.import_module(f"meu_app.plugins.{nome}")
    except ImportError:
        print(f"Plugin {nome} não encontrado, ignorando.")

# Recarregar um módulo (útil em desenvolvimento interativo)
import meu_modulo
importlib.reload(meu_modulo)

Lazy loading: importações sob demanda

Lazy loading adia a importação de módulos pesados até que sejam realmente necessários, melhorando o tempo de inicialização da aplicação:

# Sem lazy loading — tensorflow carregado mesmo se não usado
import tensorflow as tf

def treinar_modelo(dados):
    modelo = tf.keras.Sequential(...)

# Com lazy loading — tensorflow só carregado quando treinar_modelo() for chamado
def treinar_modelo(dados):
    import tensorflow as tf  # importado aqui, na primeira chamada
    modelo = tf.keras.Sequential(...)

# Abordagem mais sofisticada com __getattr__ no módulo
# meu_pacote/__init__.py
def __getattr__(nome):
    if nome == "tensorflow":
        import tensorflow
        return tensorflow
    raise AttributeError(f"Módulo não tem atributo '{nome}'")

Cache de módulos e pycache

Quando o Python importa um módulo pela primeira vez, ele compila o código para bytecode e armazena em arquivos .pyc dentro do diretório __pycache__:

meu_projeto/
├── utils.py
└── __pycache__/
    └── utils.cpython-312.pyc   # bytecode compilado para CPython 3.12

O cache de módulos vive em sys.modules. O Python reutiliza o módulo já importado em vez de ler e compilar o arquivo novamente:

import sys

# Ver todos os módulos carregados
print(list(sys.modules.keys()))

# Verificar se um módulo específico está no cache
"json" in sys.modules  # True se json foi importado

# Remover módulo do cache (força reimportação)
del sys.modules["meu_modulo"]
import meu_modulo  # reimportado do disco

Os arquivos .pyc aceleram a importação (Python não precisa compilar novamente) mas são gerados automaticamente — nunca os versione no Git. Adicione __pycache__/ e *.pyc ao .gitignore.

Termos Relacionados

  • Pacote - Coleção de módulos organizados em diretórios
  • pip - Gerenciador para instalar módulos de terceiros
  • Python - A linguagem de programação