Python Módulo Próprio

Na aula Módulos e import, você usou as caixas de ferramentas que vêm com o Python. Agora a Ana quer a sua própria caixa. Ela tem três programas na lojinha (o caixa, o controle de estoque e o de entregas), e os três precisam das mesmas contas: aplicar desconto, mostrar o valor em reais. Copiar as funções nos três arquivos dá certo até o dia em que uma conta muda e ela esquece de mudar em um deles. A solução é escrever as funções uma vez, num arquivo só, e importar esse arquivo onde precisar. Esse arquivo é um módulo seu.

Você vai aprender
  • Que todo arquivo .py é um módulo, e como importar um arquivo seu.
  • A separar o programa em dois arquivos: o módulo (as ferramentas) e o principal (que usa).
  • As formas de importar um módulo seu, e as constantes dentro dele.
  • A documentar o módulo com docstrings e ver tudo com help().
  • O que é a pasta __pycache__ que aparece sozinha.
  • A testar o módulo com if __name__ == "__main__":.
  • A guardar módulos numa pasta (um pacote).
  • Os erros reais: import precos.py, hífen no nome, o módulo na pasta errada e a função com nome errado.

A ideia: a caixa de ferramentas da casa

A Ana tem uma caixinha onde guarda as ferramentas que ela mesma usa sempre: a trena, a chave de fenda pequena, a fita isolante. Quando precisa consertar algo na cozinha ou no quarto, ela leva a mesma caixinha. Se ela compra uma trena melhor, troca dentro da caixinha, e todos os consertos dali em diante já usam a trena nova.

Em Python é igual, e mais fácil do que parece: qualquer arquivo .py já é um módulo. Se existe um arquivo precos.py na mesma pasta do seu programa, você escreve import precos (sem o .py) e usa o que está dentro dele. Não precisa instalar nada, nem registrar, nem configurar.

ArquivoPapelTem
precos.pyO módulo: a caixa de ferramentasFunções e valores (constantes), e quase nada de print
main.pyO programa principal: quem usa as ferramentasimport precos e a "história" do programa

O nome main.py (de main, "principal") é só um costume: o programa principal pode ter qualquer nome. O guia usa main.py porque é o mesmo nome da primeira aba nas caixas "Experimente".


1. O primeiro módulo

A Ana cria a pasta lojinha e, dentro dela, dois arquivos. Primeiro, a caixa de ferramentas:

"""Contas de preço da lojinha da Ana."""

TAXA_ENTREGA = 7.50


def com_desconto(preco, porcento):
    """Devolve o preço com o desconto aplicado."""
    return preco * (100 - porcento) / 100


def em_reais(valor):
    """Escreve o valor no jeito brasileiro: R$ 18,90."""
    return f"R$ {valor:.2f}".replace(".", ",")

Depois, o programa que usa:

import precos

cafe = 18.90
print("Café:", precos.em_reais(cafe))
print("Com 10% de desconto:", precos.em_reais(precos.com_desconto(cafe, 10)))
print("Entrega:", precos.em_reais(precos.TAXA_ENTREGA))
$ cd lojinha
$ ls
main.py
precos.py
$ python3 main.py
Café: R$ 18,90
Com 10% de desconto: R$ 17,01
Entrega: R$ 7,50
"""Contas de preço…"""
A docstring do módulo: um texto logo na primeira linha do arquivo, dizendo para que ele serve.
TAXA_ENTREGA = 7.50
Uma constante do módulo: um valor que os outros programas podem ler. O nome todo em MAIÚSCULAS é o costume do Python para dizer "não mude este valor".
def com_desconto(…) / def em_reais(…)
As ferramentas: funções comuns, como as da aula Funções. O módulo só as define; não chama nenhuma.
import precos
No main.py: "traga o módulo precos". O Python procura um arquivo precos.py na pasta do programa, acha, e roda o arquivo (o que cria as funções e a constante).
precos.em_reais(cafe)
Exatamente como math.sqrt: o nome do módulo, um ponto e o nome da função.
precos.TAXA_ENTREGA
A constante, lida do mesmo jeito.

O ponto principal: o main.py não sabe como o desconto é calculado. Se amanhã a conta mudar, a Ana muda no precos.py, e o caixa, o estoque e as entregas passam a usar a conta nova sem mudar uma letra.

No editor do guia: abas

Nas caixas "Experimente", cada arquivo é uma aba. A primeira aba é o programa que roda (como python3 main.py), e as outras ficam na mesma pasta, prontas para o import. Clique na aba precos.py para ver e mudar o módulo.

Missão: o main.py chama precos.com_acrescimo(100, 10), mas essa função ainda não existe (rode e veja o erro). Vá na aba precos.py e crie a função com_acrescimo(preco, porcento), parecida com a com_desconto, só que somando a porcentagem.

import precos

print("Com 10% de desconto:", precos.com_desconto(100, 10))
print("Com 10% de acréscimo:", precos.com_acrescimo(100, 10))
"""Contas de preço da lojinha da Ana."""


def com_desconto(preco, porcento):
    """Devolve o preço com o desconto aplicado."""
    return preco * (100 - porcento) / 100
"""Contas de preço da lojinha da Ana."""


def com_desconto(preco, porcento):
    """Devolve o preço com o desconto aplicado."""
    return preco * (100 - porcento) / 100


def com_acrescimo(preco, porcento):
    """Devolve o preço com o acréscimo aplicado."""
    return preco * (100 + porcento) / 100

2. As formas de importar valem para os seus módulos

Tudo o que você aprendeu com os módulos do Python funciona igual com os seus. Com from … import, os nomes ficam sem o precos. na frente:

from precos import com_desconto, em_reais

pedido = [18.90, 2.50, 5.99]
total = sum(pedido)
print("Total:", em_reais(total))
print("Com 5% de desconto:", em_reais(com_desconto(total, 5)))
$ python3 main2.py
Total: R$ 27,39
Com 5% de desconto: R$ 26,02
FormaComo se usaQuando usar
import precosprecos.em_reais(10)O padrão: fica claro que a função é sua, do precos
from precos import em_reaisem_reais(10)Poucas funções, usadas muitas vezes
import precos as pp.em_reais(10)Raramente para módulos seus: dê ao arquivo um nome bom desde o começo
from precos import *em_reais(10)Evite, pelos mesmos motivos da aula anterior

Um bom nome para o módulo

O nome do arquivo vira um nome do Python (import precos), então ele segue as mesmas regras de um nome de variável (aula Nomes de Variáveis):

  • minúsculas, com _ entre as palavras: precos.py, loja_util.py, contas_casa.py;
  • sem espaços, sem hífen (loja-util.py não dá para importar) e sem começar com número;
  • sem acento, por costume (preços.py funciona, mas complica a vida em alguns sistemas);
  • nunca o nome de um módulo do Python (random.py, json.py, math.py): o seu passa na frente e esconde o de verdade, como você viu na aula Módulos e import.

Missão: reescreva o main.py para usar from precos import … e chamar as duas funções sem o precos. na frente. A saída deve continuar a mesma.

import precos

pedido = [18.90, 2.50, 5.99]
total = sum(pedido)
print("Total:", precos.em_reais(total))
print("Com 5% de desconto:", precos.em_reais(precos.com_desconto(total, 5)))
"""Contas de preço da lojinha da Ana."""


def com_desconto(preco, porcento):
    """Devolve o preço com o desconto aplicado."""
    return preco * (100 - porcento) / 100


def em_reais(valor):
    """Escreve o valor no jeito brasileiro: R$ 18,90."""
    return f"R$ {valor:.2f}".replace(".", ",")
from precos import com_desconto, em_reais

pedido = [18.90, 2.50, 5.99]
total = sum(pedido)
print("Total:", em_reais(total))
print("Com 5% de desconto:", em_reais(com_desconto(total, 5)))

3. Documentando: docstrings e help()

As docstrings que a Ana escreveu no precos.py não são só enfeite. O help() junta todas elas num manual do módulo:

import precos

help(precos)
$ python3 ajuda.py
Help on module precos:

NAME
    precos - Contas de preço da lojinha da Ana.

FUNCTIONS
    com_desconto(preco, porcento)
        Devolve o preço com o desconto aplicado.

    em_reais(valor)
        Escreve o valor no jeito brasileiro: R$ 18,90.

DATA
    TAXA_ENTREGA = 7.5

FILE
    /home/ana/lojinha/precos.py


O manual saiu em inglês nos títulos (NAME = nome, FUNCTIONS = funções, DATA = dados, FILE = arquivo), mas o conteúdo é o que a Ana escreveu: a docstring do módulo na primeira linha, cada função com os seus parâmetros e a sua docstring, a constante com o valor. É o mesmo manual que você vê com help(math). Num terminal de verdade, ele abre numa tela própria; aperte q para sair.


4. A pasta __pycache__

Depois de rodar o main.py pela primeira vez, apareceu uma pasta que a Ana não criou:

$ ls
__pycache__
main.py
precos.py
$ tree
.
├── main.py
├── precos.py
└── __pycache__
    └── precos.cpython-314.pyc

2 directories, 3 files

Quando um módulo é importado, o Python o traduz para um formato próprio, mais rápido de carregar (o bytecode), e guarda o resultado em __pycache__ (de cache, "esconderijo", um lugar para guardar coisas prontas). O nome precos.cpython-314.pyc diz: o precos, traduzido pelo CPython 3.14, em formato .pyc (Python compiled).

  • Na próxima vez, se o precos.py não mudou, o Python usa a tradução pronta. Se mudou, ele traduz de novo sozinho.
  • Repare que só o módulo ganhou .pyc: o programa principal (main.py) é traduzido toda vez e não fica guardado.
  • Pode apagar a pasta quando quiser: ela volta na próxima execução. E não precisa mandá-la junto quando passar o programa para alguém.

5. Testando o módulo com if __name__ == "__main__":

Enquanto escreve um módulo, dá vontade de testar as funções ali mesmo, no fim do arquivo. Mas você viu na aula Módulos e import que importar roda o arquivo: um print de teste solto no módulo aparece também no programa que o importa. A solução é guardar os testes no bloco que só roda quando o módulo é o principal:

"""Contas de preço da lojinha da Ana."""

TAXA_ENTREGA = 7.50


def com_desconto(preco, porcento):
    """Devolve o preço com o desconto aplicado."""
    return preco * (100 - porcento) / 100


def em_reais(valor):
    """Escreve o valor no jeito brasileiro: R$ 18,90."""
    return f"R$ {valor:.2f}".replace(".", ",")


if __name__ == "__main__":
    print("Testando o módulo precos")
    print(com_desconto(100, 10), "deveria ser 90.0")
    print(em_reais(1234.5), "deveria ser R$ 1234,50")
$ python3 precos.py                     « rodando o módulo sozinho: os testes rodam
Testando o módulo precos
90.0 deveria ser 90.0
R$ 1234,50 deveria ser R$ 1234,50
$ python3 main.py                       « importado: os testes ficam quietos
Café: R$ 18,90
Com 10% de desconto: R$ 17,01
Entrega: R$ 7,50

Com python3 precos.py, o __name__ do módulo vale "__main__" e os testes rodam. Importado pelo main.py, ele vale "precos", e o bloco é pulado. Os testes "de verdade", que conferem sozinhos se o resultado está certo, ficam para a aula Testes.


6. Módulos numa pasta: pacotes

Quando os módulos se multiplicam, a Ana pode guardá-los numa subpasta, como gavetas numa cômoda. Uma pasta de módulos se chama pacote (package). Ela cria a pasta util e coloca o precos.py lá dentro:

$ mkdir util
$ mv precos.py util/
$ tree
.
├── main_pacote.py
├── main.py
└── util
    └── precos.py

2 directories, 3 files

Para importar, o caminho usa ponto no lugar da barra: "da pasta util, traga o precos":

from util import precos

print("Café:", precos.em_reais(18.90))
$ python3 main_pacote.py
Café: R$ 18,90

Também valem import util.precos (e aí se usa util.precos.em_reais(…)) e from util.precos import em_reais. É assim que os pacotes grandes se organizam: from os import path, import json.decoder.

E o __init__.py?

Em muitos projetos, você vai ver dentro da pasta de um pacote um arquivo chamado __init__.py, quase sempre vazio. Ele marca a pasta como pacote "de verdade" e roda na primeira vez que o pacote é importado. Desde o Python 3.3 ele não é obrigatório (o exemplo acima funcionou sem ele), mas criar um __init__.py vazio continua sendo o costume, e algumas ferramentas esperam encontrá-lo.

As abas do editor ficam todas na mesma pasta

No editor do guia, os arquivos das abas vão todos para a pasta do programa, sem subpastas. Os pacotes, por isso, você experimenta no seu terminal; nas caixas "Experimente" desta aula, os módulos ficam ao lado do main.py.


Quando dá errado

import precos.py, com a extensão
import precos.py
$ python3 main_ponto.py
Traceback (most recent call last):
  File "/home/ana/lojinha/main_ponto.py", line 1, in <module>
    import precos.py
ModuleNotFoundError: No module named 'precos.py'; 'precos' is not a package

"Não existe módulo chamado 'precos.py'; 'precos' não é um pacote." O ponto, no import, quer dizer "de dentro de" (como no from util import precos). Então o Python achou o precos e foi procurar, dentro dele, um módulo chamado py. Como precos é um arquivo, e não uma pasta, ele reclama. Como consertar: import precos, sem o .py. O nome do módulo é o nome do arquivo sem a extensão.

Hífen no nome do arquivo: SyntaxError
import loja-util
$ python3 main_hifen.py
  File "/home/ana/lojinha/main_hifen.py", line 1
    import loja-util
               ^
SyntaxError: invalid syntax

"Sintaxe inválida", com o ^ embaixo do hífen. Para o Python, o - é o sinal de menos: ele leu "importe loja, menos util", o que não faz sentido. Repare que não há Traceback: o erro é de escrita (o programa nem começou a rodar). Como consertar: renomeie o arquivo para loja_util.py, com sublinhado, e importe com import loja_util.

Nome do arquivo errado: ModuleNotFoundError
import preco

print(preco.em_reais(10))
$ python3 main_errado.py
Traceback (most recent call last):
  File "/home/ana/lojinha/main_errado.py", line 1, in <module>
    import preco
ModuleNotFoundError: No module named 'preco'

O arquivo se chama precos.py, com s; não existe nenhum preco.py. Como consertar: o nome no import tem de ser igual ao nome do arquivo, letra por letra (inclusive maiúsculas: Precos.py e precos.py são arquivos diferentes no Linux).

O módulo em outra pasta: ModuleNotFoundError

Na seção 6, a Ana moveu o precos.py para a pasta util. E o main.py antigo, que fazia só import precos?

$ python3 lojinha/main.py
Traceback (most recent call last):
  File "/home/ana/lojinha/main.py", line 1, in <module>
    import precos
ModuleNotFoundError: No module named 'precos'

O Python procura os módulos na pasta do programa (aqui, lojinha), na biblioteca padrão e nos pacotes instalados. Ele não vasculha as subpastas por conta própria. Como consertar: deixe o módulo ao lado do programa, ou importe com o caminho do pacote: from util import precos.

Repare na primeira linha: a Ana rodou python3 lojinha/main.py de fora da pasta, e isso não importa. A pasta que conta é a do main.py, e não a pasta em que o terminal está. Antes de mover o módulo, rodar de fora funcionava normalmente:

$ python3 lojinha/main.py
Café: R$ 18,90
Com 10% de desconto: R$ 17,01
Entrega: R$ 7,50
Função com nome errado: AttributeError com dica
import precos

print(precos.em_real(10))
$ python3 main_func.py
Traceback (most recent call last):
  File "/home/ana/lojinha/main_func.py", line 3, in <module>
    print(precos.em_real(10))
          ^^^^^^^^^^^^^^
AttributeError: module 'precos' has no attribute 'em_real'. Did you mean: 'em_reais'?

"O módulo precos não tem o atributo em_real. Você quis dizer 'em_reais'?" O Python olhou os nomes que existem no seu módulo e achou um parecido. Como consertar: precos.em_reais(10).


Referência rápida

EscritaO que faz
precos.py na pasta do programaJá é um módulo: import precos
import precosRoda o precos.py (uma vez) e dá acesso a precos.nome
from precos import em_reaisTraz só esse nome
"""Docstring.""" na 1ª linha do arquivoExplica o módulo; aparece no help()
NOME_EM_MAIUSCULAS = valorConstante do módulo (por costume, não se muda)
help(precos)O manual do módulo, montado das docstrings
if __name__ == "__main__":Bloco que só roda com python3 precos.py, não no import
__pycache__/precos.cpython-314.pycA tradução pronta do módulo; pode apagar
from util import precosImporta o util/precos.py (pacote util)
util/__init__.pyMarca a pasta como pacote (opcional, mas costume)
MensagemCausa provável
No module named 'precos.py'; 'precos' is not a packageEscreveu o .py no import
SyntaxError: invalid syntax no importHífen ou espaço no nome do arquivo
ModuleNotFoundError: No module named 'x'Nome diferente do arquivo, ou o arquivo está em outra pasta
AttributeError: module 'precos' has no attribute 'x'A função não existe no módulo (nome errado, ou ainda não foi criada)
Os testes do módulo aparecem no programa principalFaltou o if __name__ == "__main__":

Exercícios

1. Importando o arquivo

A Ana criou o arquivo contas_casa.py. Complete o import:

import

2. Usando a função

Depois de import precos, complete para chamar a função em_reais do módulo:

print((10))

3. Da subpasta

O módulo está em util/precos.py. Complete:

util precos

4. Qual destes nomes de arquivo dá para importar sem problemas?

5. A pasta __pycache__ apareceu depois que a Ana rodou o programa. O que fazer com ela?

6. O precos.py termina com print("teste", com_desconto(100, 10)), sem if. O que acontece quando o main.py faz import precos?

Missão final: o módulo das notas

Missão: o main.py está pronto e usa duas funções do módulo notas, que está vazio. Na aba notas.py, escreva:

  • media(lista): devolve a média da lista (a soma dividida pela quantidade);
  • situacao(valor): devolve "aprovada" se o valor for 7 ou mais, e "em recuperação" se for menos.
import notas

turma = {"Bia": [8.0, 7.5, 8.5], "Caio": [5.0, 6.0, 5.5]}

for nome, lista in turma.items():
    m = notas.media(lista)
    print(f"{nome}: média {m:.1f}, {notas.situacao(m)}")
"""Contas com as notas da turma."""

# escreva as funções aqui
"""Contas com as notas da turma."""


def media(lista):
    """Devolve a média dos números da lista."""
    return sum(lista) / len(lista)


def situacao(valor):
    """Diz se a média aprova ou leva para a recuperação."""
    if valor >= 7:
        return "aprovada"
    return "em recuperação"

Experimente também acrescentar um if __name__ == "__main__": com testes no fim do notas.py, e confira que eles não aparecem quando você roda a caixa.


Resumo

  • Todo arquivo .py é um módulo. Com precos.py na pasta do programa, import precos (sem o .py) dá acesso a precos.com_desconto, precos.TAXA_ENTREGA…
  • Separe as ferramentas (funções e constantes, no módulo) da história (o programa principal). Uma conta escrita uma vez serve a vários programas.
  • Nomes de módulo: minúsculas com _, sem hífen, sem espaço, nunca o nome de um módulo do Python.
  • Docstrings no módulo e nas funções viram o manual do help().
  • A pasta __pycache__ guarda a tradução pronta dos módulos; pode apagar.
  • Testes do módulo vão dentro do if __name__ == "__main__":, para não vazar no programa que importa.
  • Uma pasta de módulos é um pacote: from util import precos.