W3docs

Pacotes Python e o Sistema de Importação

Aprenda como funcionam os pacotes Python: crie um pacote com __init__.py, use importações absolutas e relativas, exponha uma API pública limpa e evite erros comuns.

Um pacote é um diretório de módulos Python que você trata como uma única unidade importável. Enquanto um módulo é um único arquivo .py, um pacote é uma pasta — potencialmente contendo muitos módulos e sub-pacotes — que o sistema de importação do Python pode navegar como uma árvore. Este capítulo explica como criar pacotes, controlar o que eles expõem, escrever importações absolutas e relativas corretamente, e evitar as armadilhas que costumam pegar iniciantes de surpresa.

Módulos vs. Pacotes — a Diferença Principal

Um módulo é um único arquivo .py:

greetings.py        ← module

Um pacote é um diretório que contém pelo menos um arquivo especial chamado __init__.py:

greetings/          ← package
    __init__.py
    english.py
    spanish.py

Ambos são importados com a mesma palavra-chave import, mas um pacote oferece uma hierarquia de namespaces: greetings.english e greetings.spanish são módulos separados, mas compartilham o namespace greetings.

Quando usar um módulo vs. um pacote:

SituaçãoUse
Um utilitário pequeno e independenteMódulo (um arquivo .py)
Vários módulos relacionados que você quer sob um único nomePacote (um diretório)
Uma biblioteca que você pretende distribuir no PyPIPacote (com layout src/)

O Arquivo __init__.py

__init__.py é o que transforma um diretório em um pacote. O Python o executa na primeira vez que o pacote (ou qualquer um de seus módulos) é importado. Pode estar vazio, ou pode:

  • Importar nomes de sub-módulos para torná-los disponíveis no nível do pacote
  • Executar a inicialização no nível do pacote (configuração de logging, verificações de versão, etc.)
  • Definir __all__ para controlar from package import *

Layout mínimo de um pacote

myapp/
    __init__.py       ← can be empty
    utils.py
    config.py
# myapp/__init__.py  (empty — that is fine)
# myapp/utils.py
def greet(name):
    return f"Hello, {name}!"

Importar de fora do pacote:

from myapp.utils import greet

print(greet("Alice"))   # Hello, Alice!

Expondo nomes no nível do pacote

Um padrão comum é importar os nomes mais usados no __init__.py para que os chamadores possam escrever from myapp import greet em vez de from myapp.utils import greet.

# myapp/__init__.py
from .utils import greet
from .config import MAX_RETRIES

Agora ambos os nomes estão disponíveis diretamente no pacote:

import myapp

print(myapp.greet("Bob"))   # Hello, Bob!
print(myapp.MAX_RETRIES)    # whatever config.py defines

Importações Absolutas

Uma importação absoluta sempre começa pelo pacote de nível superior ou por um diretório em sys.path. Ela nunca depende de onde está o arquivo que realiza a importação.

project/
    myapp/
        __init__.py
        utils.py
        services/
            __init__.py
            email.py

Dentro de email.py, uma importação absoluta tem a seguinte forma:

# myapp/services/email.py
from myapp.utils import greet   # absolute — starts from the top-level package

def send_welcome(user):
    message = greet(user)
    print(f"Sending: {message}")

As importações absolutas são o estilo padrão e recomendado (PEP 8). Elas são inequívocas independentemente de como o Python é invocado.

Importações Relativas

Uma importação relativa usa pontos (.) para navegar pela árvore do pacote em relação à localização do arquivo atual.

  • . significa o pacote atual
  • .. significa o pacote pai
  • ... significa o pacote avô, e assim por diante
# myapp/services/email.py

# One dot — import from myapp.services (same directory)
from . import sms

# Two dots — import from myapp (parent directory)
from ..utils import greet

Quando usar importações relativas

As importações relativas são úteis dentro de um pacote quando você quer deixar claro que greet vem deste pacote e não de alguma biblioteca externa com o mesmo nome. Elas também facilitam a refatoração porque as importações acompanham o pacote caso você renomeie o diretório de nível superior.

Atenção: as importações relativas só funcionam dentro de um pacote. Se você executar python myapp/utils.py diretamente, o Python o trata como um script independente, não como parte de um pacote, e uma importação relativa lança ImportError: attempted relative import with no known parent package. Execute o pacote com python -m myapp.utils em vez disso.

# Wrong — runs utils.py as a script, breaking relative imports
$ python myapp/utils.py

# Right — runs utils.py as part of the myapp package
$ python -m myapp.utils

Controlando a API Pública com __all__

__all__ é uma lista de nomes que from package import * exporta. Ela também documenta o que o pacote considera público.

# myapp/__init__.py
from .utils import greet, farewell
from .config import MAX_RETRIES

__all__ = ["greet", "MAX_RETRIES"]   # farewell is intentionally not exported

Agora from myapp import * traz apenas greet e MAX_RETRIES. A função farewell ainda existe; ela simplesmente não faz parte da interface pública anunciada. Nomes prefixados com um único sublinhado (_private) também são excluídos de import * por convenção.

Pacotes Aninhados (Sub-pacotes)

Pacotes podem conter outros pacotes. Cada subdiretório precisa de seu próprio __init__.py.

analytics/
    __init__.py
    reports/
        __init__.py
        daily.py
        weekly.py
    charts/
        __init__.py
        bar.py
        pie.py

Importe um módulo profundamente aninhado usando o caminho completo com pontos:

from analytics.reports.daily import generate_report
from analytics.charts.bar import BarChart

Ou, se analytics/__init__.py os expõe:

# analytics/__init__.py
from .reports.daily import generate_report
# caller
from analytics import generate_report

Qual profundidade é adequada?

Um pacote com três ou quatro níveis de profundidade geralmente é sinal de que cresceu demais e deve ser dividido em pacotes separados de nível superior (instaláveis independentemente). Para a maioria dos projetos, dois níveis (package.module) são suficientes.

Um Exemplo Prático: Construindo um Pacote geometry

Vamos construir um pacote pequeno mas realista, passo a passo.

Layout de diretório

geometry/
    __init__.py
    shapes.py
    conversions.py

shapes.py

# geometry/shapes.py
import math

def circle_area(radius):
    """Return the area of a circle with the given radius."""
    if radius < 0:
        raise ValueError("radius must be non-negative")
    return math.pi * radius ** 2

def rectangle_area(width, height):
    """Return the area of a rectangle."""
    return width * height

def triangle_area(base, height):
    """Return the area of a triangle."""
    return 0.5 * base * height

conversions.py

# geometry/conversions.py

def degrees_to_radians(degrees):
    """Convert degrees to radians."""
    import math
    return degrees * math.pi / 180

def radians_to_degrees(radians):
    """Convert radians to degrees."""
    import math
    return radians * 180 / math.pi

__init__.py — expondo os nomes principais

# geometry/__init__.py
"""
geometry — simple 2-D geometry utilities.

Public API:
    circle_area(radius) -> float
    rectangle_area(width, height) -> float
    triangle_area(base, height) -> float
    degrees_to_radians(degrees) -> float
    radians_to_degrees(radians) -> float
"""

from .shapes import circle_area, rectangle_area, triangle_area
from .conversions import degrees_to_radians, radians_to_degrees

__all__ = [
    "circle_area",
    "rectangle_area",
    "triangle_area",
    "degrees_to_radians",
    "radians_to_degrees",
]

Usando o pacote

# main.py (sits next to the geometry/ directory)
import geometry

print(geometry.circle_area(5))           # 78.53981633974483
print(geometry.rectangle_area(4, 6))     # 24
print(geometry.degrees_to_radians(90))   # 1.5707963267948966

Ou com importações seletivas:

from geometry import circle_area, degrees_to_radians

print(circle_area(3))              # 28.274333882308138
print(degrees_to_radians(180))     # 3.141592653589793

Pacotes de Namespace (Python 3.3+)

Desde o Python 3.3, um diretório sem __init__.py é um pacote de namespace. O Python mescla todos os diretórios com o mesmo nome em sys.path em um único pacote lógico. Isso é principalmente útil para grandes organizações que dividem um único pacote entre vários repositórios ou diretórios de instalação.

Para o desenvolvimento cotidiano, sempre inclua __init__.py. Isso deixa sua intenção inequívoca e funciona em todas as versões do Python.

Como o Python Encontra Pacotes

Quando você escreve import geometry, o Python pesquisa sys.path em ordem:

  1. O diretório do script em execução (ou o diretório atual no modo interativo)
  2. Diretórios na variável de ambiente PYTHONPATH
  3. Os diretórios da biblioteca padrão
  4. O diretório site-packages (onde ficam os pacotes instalados pelo pip)
import sys
print(sys.path)

O diretório do pacote deve estar diretamente dentro de um desses locais. Se geometry/ estiver em /home/alice/projects/, o Python não o encontrará, a menos que /home/alice/projects/ esteja em sys.path.

Dica: use um ambiente virtual e instale seu pacote em modo de desenvolvimento (pip install -e .) para que o Python sempre o encontre sem manipulação manual de sys.path.

Distribuindo um Pacote

Para compartilhar um pacote com outras pessoas (ou instalá-lo com pip), você precisa de um pyproject.toml na raiz do projeto:

my_project/
    pyproject.toml    ← build metadata
    src/
        geometry/
            __init__.py
            shapes.py
            conversions.py

Um pyproject.toml mínimo:

[build-system]
requires = ["setuptools>=68", "wheel"]
build-backend = "setuptools.backends.legacy:build"

[project]
name = "geometry"
version = "0.1.0"
description = "Simple 2-D geometry utilities"
requires-python = ">=3.9"

Instale localmente em modo editável durante o desenvolvimento:

pip install -e .

Agora import geometry funciona em qualquer lugar no seu ambiente virtual, independentemente do diretório atual.

Erros Comuns

__init__.py ausente

Se você esquecer o __init__.py, o Python 3 trata o diretório como um pacote de namespace (o que geralmente ainda funciona), mas o Python 2 o ignora completamente. Seja explícito: sempre adicione __init__.py.

Nomear um pacote igual a um módulo da stdlib

Evite nomes como math/, json/, os/, email/. O Python pode importar seu pacote em vez do da biblioteca padrão, quebrando código não relacionado.

Executar um módulo de pacote como script

Como mencionado acima, executar python myapp/services/email.py diretamente quebra as importações relativas. Use python -m myapp.services.email em vez disso.

Importações circulares entre módulos do mesmo pacote

Se shapes.py importa de conversions.py e conversions.py importa de shapes.py, você tem uma importação circular. Os sintomas incluem ImportError ou nomes que aparecem inesperadamente como None. A solução geralmente é mover a lógica compartilhada para um terceiro módulo, ou atrasar a importação para dentro do corpo de uma função.

# Delayed import — breaks the cycle at module load time
def some_function():
    from .shapes import circle_area   # imported only when the function is called
    ...

ImportError ao usar importações relativas fora de um pacote

# Will raise: ImportError: attempted relative import with no known parent package
# if run as:  python myapp/utils.py

from . import config   # relative import inside utils.py

Execute com python -m myapp.utils ou reestruture para que o ponto de entrada seja um script separado que importe o pacote.

Resumo

ConceitoResumo
PacoteUm diretório com __init__.py contendo módulos
__init__.pyTransforma um diretório em pacote; executado na primeira importação
Importação absolutafrom myapp.utils import greet — sempre a partir da raiz
Importação relativafrom ..utils import greet — relativa ao arquivo atual
__all__Lista nomes exportados por from package import *
Pacote de namespaceDiretório sem __init__.py; apenas Python 3.3+
Instalação editávelpip install -e . — pacote encontrado em qualquer lugar no venv

Veja Python Modules para organização de código em arquivo único, Python pip para instalar pacotes de terceiros, e Python Virtual Environments para manter as dependências do projeto isoladas.

Prática

Prática
What makes a directory a Python package (in Python versions before 3.3)?
What makes a directory a Python package (in Python versions before 3.3)?
Was this page helpful?