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.
- 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.
| Arquivo | Papel | Tem |
|---|---|---|
precos.py | O módulo: a caixa de ferramentas | Funções e valores (constantes), e quase nada de print |
main.py | O programa principal: quem usa as ferramentas | import 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óduloprecos". O Python procura um arquivoprecos.pyna 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
| Forma | Como se usa | Quando usar |
|---|---|---|
import precos | precos.em_reais(10) | O padrão: fica claro que a função é sua, do precos |
from precos import em_reais | em_reais(10) | Poucas funções, usadas muitas vezes |
import precos as p | p.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.pynão dá para importar) e sem começar com número; - sem acento, por costume (
preços.pyfunciona, 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.pynã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.
__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.
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.
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.
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).
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
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
| Escrita | O que faz |
|---|---|
precos.py na pasta do programa | Já é um módulo: import precos |
import precos | Roda o precos.py (uma vez) e dá acesso a precos.nome |
from precos import em_reais | Traz só esse nome |
"""Docstring.""" na 1ª linha do arquivo | Explica o módulo; aparece no help() |
NOME_EM_MAIUSCULAS = valor | Constante 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.pyc | A tradução pronta do módulo; pode apagar |
from util import precos | Importa o util/precos.py (pacote util) |
util/__init__.py | Marca a pasta como pacote (opcional, mas costume) |
| Mensagem | Causa provável |
|---|---|
No module named 'precos.py'; 'precos' is not a package | Escreveu o .py no import |
SyntaxError: invalid syntax no import | Hí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 principal | Faltou o if __name__ == "__main__": |
Exercícios
1. Importando o arquivo
A Ana criou o arquivo contas_casa.py. Complete o import:
2. Usando a função
Depois de import precos, complete para chamar a função em_reais do módulo:
3. Da subpasta
O módulo está em util/precos.py. Complete:
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. Comprecos.pyna pasta do programa,import precos(sem o.py) dá acesso aprecos.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.