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.