Fundamentos do Python para Iniciantes

0/31 aulas0%
projeto

Criando e Importando Módulos Customizados

Aprenda sobre criando e importando módulos customizados

50 min
Aula 5 de 5

Criando e Importando Módulos Customizados em Python 📦

Olá, futuro(a) desenvolvedor(a) Python! 👋

Até agora, aprendemos sobre funções e como elas nos ajudam a organizar e reutilizar blocos de código. Mas e se quisermos organizar um conjunto maior de funções, variáveis e classes que estão relacionadas a uma funcionalidade específica? É aí que entram os módulos!

Nesta aula, vamos mergulhar no mundo dos módulos customizados em Python. Você aprenderá a:

  • Criar seus próprios arquivos de módulo (.py).
  • Importar esses módulos para outros scripts.
  • Utilizar diferentes formas de importação (import, from ... import, as).
  • Entender o papel da variável especial __name__.

Ao final, aplicaremos tudo isso em um projeto prático para consolidar seu conhecimento! Vamos lá? ✨


1. O que são Módulos em Python? 🧩

Em Python, um módulo é simplesmente um arquivo que contém definições e declarações Python. Isso pode incluir:

  • Funções
  • Classes
  • Variáveis
  • Outras instruções

Pense em um módulo como uma "caixa de ferramentas" ou uma "biblioteca" de código relacionado. Ao dividir seu código em módulos, você ganha diversos benefícios:

  • Reusabilidade: Você pode usar o mesmo código em diferentes programas sem copiá-lo.
  • Organização: Mantém seu código limpo, legível e bem estruturado.
  • Evita Conflitos de Nomes: Cada módulo tem seu próprio namespace, o que ajuda a evitar que nomes de variáveis ou funções colidam.

A própria biblioteca padrão do Python é composta por inúmeros módulos (como math, random, os, sys, etc.) que usamos o tempo todo! Agora, você aprenderá a criar os seus.


2. Criando e Importando Seu Primeiro Módulo 🚀

Vamos criar um módulo simples para realizar algumas operações matemáticas básicas.

2.1. Criando o Arquivo do Módulo

Crie um novo arquivo chamado operacoes.py em seu diretório de trabalho.

# operacoes.py
 
def soma(a, b):
    """Retorna a soma de dois números."""
    return a + b
 
def subtracao(a, b):
    """Retorna a subtração de dois números."""
    return a - b
 
def multiplicacao(a, b):
    """Retorna a multiplicação de dois números."""
    return a * b
 
def divisao(a, b):
    """Retorna a divisão de dois números, tratando divisão por zero."""
    if b == 0:
        raise ValueError("Não é possível dividir por zero!")
    return a / b
 
PI = 3.14159 # Uma variável global no módulo

2.2. Importando o Módulo Completo

Agora, crie outro arquivo no mesmo diretório chamado main.py. Este será o seu script principal que usará as funções do módulo operacoes.

# main.py
 
# Importa o módulo completo
import operacoes
 
print("--- Usando import operacoes ---")
print(f"Soma de 5 e 3: {operacoes.soma(5, 3)}")
print(f"Subtração de 10 e 4: {operacoes.subtracao(10, 4)}")
print(f"Multiplicação de 6 e 7: {operacoes.multiplicacao(6, 7)}")
print(f"Divisão de 20 e 5: {operacoes.divisao(20, 5)}")
 
# Acessando a variável PI do módulo
print(f"Valor de PI do módulo: {operacoes.PI}")
 
try:
    print(f"Divisão por zero: {operacoes.divisao(10, 0)}")
except ValueError as e:
    print(f"Erro: {e}")
 
print("\n--- Fim do import operacoes ---")

Como funciona:

  • import operacoes: Isso importa todo o módulo operacoes.
  • Para acessar qualquer função, variável ou classe dentro do módulo, você deve prefixar o nome com operacoes. (ex: operacoes.soma). Isso garante que você saiba de onde a função ou variável está vindo, evitando conflitos de nomes se você tiver uma função soma em outro lugar.

2.3. Importando Itens Específicos (from ... import)

Se você precisa usar apenas algumas funções ou variáveis de um módulo e não quer prefixar seus nomes a cada uso, pode importá-las diretamente.

Modifique seu main.py ou crie um novo arquivo para testar:

# main.py (continuando ou novo arquivo)
 
# Importa apenas as funções soma e multiplicacao do módulo
from operacoes import soma, multiplicacao, PI
 
print("\n--- Usando from operacoes import soma, multiplicacao, PI ---")
print(f"Soma de 8 e 2: {soma(8, 2)}") # Não precisa de 'operacoes.'
print(f"Multiplicação de 4 e 5: {multiplicacao(4, 5)}") # Não precisa de 'operacoes.'
print(f"Valor de PI diretamente: {PI}")
 
# Tentar usar subtracao sem importá-la diretamente resultará em erro
# print(f"Subtração de 10 e 3: {subtracao(10, 3)}") # Isso causaria um NameError
 
print("\n--- Fim do from operacoes import ---")

Como funciona:

  • from operacoes import soma, multiplicacao, PI: Isso importa soma, multiplicacao e PI diretamente para o namespace do seu script atual.
  • Você pode usar soma e multiplicacao e PI diretamente, sem o prefixo operacoes..
  • Cuidado: Isso pode levar a conflitos de nomes se você tiver outra função ou variável com o mesmo nome em seu script.

2.4. Importando com Apelidos (Aliases) (as)

Às vezes, o nome de um módulo é muito longo, ou você quer evitar um conflito de nomes. Você pode usar a palavra-chave as para dar um apelido (alias) ao módulo ou aos itens importados.

# main.py (continuando ou novo arquivo)
 
# Importa o módulo completo com um apelido
import operacoes as op
 
print("\n--- Usando import operacoes as op ---")
print(f"Soma de 12 e 3: {op.soma(12, 3)}")
print(f"Subtração de 20 e 8: {op.subtracao(20, 8)}")
 
# Importa uma função específica com um apelido
from operacoes import divisao as div
 
print(f"Divisão de 100 e 10 (com alias): {div(100, 10)}")
 
print("\n--- Fim do import ... as ---")

Como funciona:

  • import operacoes as op: Agora você se refere ao módulo operacoes como op.
  • from operacoes import divisao as div: A função divisao agora é acessível como div.

2.5. Importando Tudo (Wildcard Import) (from ... import *) ⚠️

Existe uma forma de importar tudo de um módulo para o namespace atual usando o asterisco (*).

# main.py (continuando ou novo arquivo)
 
# NÃO RECOMENDADO PARA CÓDIGO DE PRODUÇÃO!
from operacoes import *
 
print("\n--- Usando from operacoes import * (NÃO RECOMENDADO!) ---")
print(f"Soma de 7 e 7: {soma(7, 7)}")
print(f"PI direto: {PI}")
 
# Todas as funções e variáveis de 'operacoes' estão agora no namespace global
# Isso pode causar confusão e conflitos de nomes.
print("\n--- Fim do from operacoes import * ---")

Por que não é recomendado?

  • Poluição de Namespace: Você não sabe quais nomes foram importados, o que pode levar a sobrescrita acidental de suas próprias variáveis ou funções.
  • Dificuldade de Debugging: Torna mais difícil rastrear a origem de uma função ou variável.
  • Legibilidade Reduzida: O código fica menos claro sobre de onde cada item vem.

Use com extrema cautela e apenas em casos muito específicos (como em alguns scripts interativos ou para fins de teste rápido).


3. O Caminho de Busca de Módulos (sys.path) 🗺️

Quando você tenta importar um módulo, o Python não o encontra por mágica. Ele segue uma sequência de diretórios para localizar o arquivo .py correspondente. Essa sequência é conhecida como caminho de busca de módulos e pode ser inspecionada usando o módulo sys.

# main.py (continuando ou novo arquivo)
 
import sys
 
print("\n--- Caminho de Busca de Módulos (sys.path) ---")
for path in sys.path:
    print(path)
print("--- Fim do sys.path ---")

A ordem de busca geralmente é:

  1. O diretório do script de entrada (o script que você está executando).
  2. A lista de diretórios em sua variável de ambiente PYTHONPATH.
  3. Os diretórios de instalação da biblioteca padrão do Python.
  4. Os diretórios de pacotes de terceiros (site-packages).

É por isso que operacoes.py e main.py precisam estar no mesmo diretório para que main.py encontre operacoes.py sem configurações adicionais.


4. A Variável Especial __name__ 🕵️‍♂️

Todo módulo Python tem uma variável especial embutida chamada __name__. Seu valor depende de como o módulo é executado:

  • Se o módulo for executado diretamente (como o script principal), __name__ será definido como "__main__".
  • Se o módulo for importado por outro script, __name__ será definido como o nome do módulo (neste caso, "operacoes").

Isso é extremamente útil para incluir código de teste ou de inicialização dentro de um módulo que só deve ser executado quando o módulo é o script principal, e não quando é importado.

Vamos modificar operacoes.py para incluir um bloco if __name__ == "__main__"::

# operacoes.py
 
def soma(a, b):
    """Retorna a soma de dois números."""
    return a + b
 
def subtracao(a, b):
    """Retorna a subtração de dois números."""
    return a - b
 
def multiplicacao(a, b):
    """Retorna a multiplicação de dois números."""
    return a * b
 
def divisao(a, b):
    """Retorna a divisão de dois números, tratando divisão por zero."""
    if b == 0:
        raise ValueError("Não é possível dividir por zero!")
    return a / b
 
PI = 3.14159
 
# Este bloco só será executado se operacoes.py for o script principal
if __name__ == "__main__":
    print("--- Executando operacoes.py diretamente ---")
    print(f"Teste de soma: {soma(10, 5)}")
    print(f"Teste de PI: {PI}")
    print("--- Fim da execução direta de operacoes.py ---")

Teste:

  1. Abra seu terminal e execute python operacoes.py. Você verá as mensagens de teste.
  2. Agora, execute python main.py (com qualquer uma das importações que fizemos anteriormente). Você não verá as mensagens de teste de operacoes.py, pois ele está sendo importado, e não executado diretamente.

Este é um padrão muito comum e importante em Python para escrever código modular e testável!


5. Projeto: Gerenciador de Tarefas Simples 📝

Vamos aplicar o que aprendemos para construir um pequeno "Gerenciador de Tarefas" que usa módulos customizados para organizar suas funcionalidades.

Objetivo: Criar um programa simples de linha de comando para adicionar, listar e marcar tarefas como concluídas.

Estrutura do Projeto

Crie uma pasta chamada gerenciador_tarefas e, dentro dela, os seguintes arquivos:

gerenciador_tarefas/
├── main.py
├── tarefas.py
└── utils.py

Tarefas do Projeto (Checklist)

Aqui está o que você precisa fazer:

🎯 Parte 1: tarefas.py - Lógica de Negócios

Este módulo conterá as funções principais para manipular as tarefas.

  • Crie o arquivo tarefas.py.
  • Defina uma lista global (ou que será passada) para armazenar as tarefas. Cada tarefa pode ser um dicionário com {'id': int, 'descricao': str, 'concluida': bool}.
  • Crie a função adicionar_tarefa(lista_tarefas, descricao):
    • Adiciona uma nova tarefa à lista_tarefas.
    • Gere um id único (pode ser o comprimento atual da lista + 1, ou um contador).
    • Defina concluida como False por padrão.
  • Crie a função listar_tarefas(lista_tarefas):
    • Imprime todas as tarefas de forma formatada, indicando se estão concluídas ou não.
    • Exemplo: [ ] 1. Comprar leite ou [X] 2. Estudar Python.
  • Crie a função marcar_concluida(lista_tarefas, id_tarefa):
    • Encontra a tarefa pelo id_tarefa e define seu status concluida como True.
    • Lide com o caso de o id_tarefa não ser encontrado.
  • (Opcional) Adicione um bloco if __name__ == "__main__": para testar as funções deste módulo isoladamente.
# gerenciador_tarefas/tarefas.py
 
lista_de_tarefas = []
proximo_id = 1
 
def adicionar_tarefa(descricao):
    """Adiciona uma nova tarefa à lista."""
    global proximo_id
    tarefa = {
        'id': proximo_id,
        'descricao': descricao,
        'concluida': False
    }
    lista_de_tarefas.append(tarefa)
    proximo_id += 1
    print(f"Tarefa '{descricao}' adicionada com ID {tarefa['id']}.")
 
def listar_tarefas():
    """Lista todas as tarefas."""
    if not lista_de_tarefas:
        print("Nenhuma tarefa para exibir.")
        return
 
    print("\n--- SUAS TAREFAS ---")
    for tarefa in lista_de_tarefas:
        status = "[X]" if tarefa['concluida'] else "[ ]"
        print(f"{status} {tarefa['id']}. {tarefa['descricao']}")
    print("--------------------")
 
def marcar_concluida(id_tarefa):
    """Marca uma tarefa como concluída pelo seu ID."""
    for tarefa in lista_de_tarefas:
        if tarefa['id'] == id_tarefa:
            tarefa['concluida'] = True
            print(f"Tarefa '{tarefa['descricao']}' (ID {id_tarefa}) marcada como concluída.")
            return
    print(f"Erro: Tarefa com ID {id_tarefa} não encontrada.")
 
if __name__ == "__main__":
    print("--- Testando o módulo tarefas.py diretamente ---")
    adicionar_tarefa("Comprar pão")
    adicionar_tarefa("Estudar Python")
    listar_tarefas()
    marcar_concluida(1)
    listar_tarefas()
    adicionar_tarefa("Fazer exercícios")
    marcar_concluida(99) # Testando ID inválido
    listar_tarefas()
    print("--- Fim do teste de tarefas.py ---")
 

🎯 Parte 2: utils.py - Funções Utilitárias

Este módulo conterá funções auxiliares, como salvar e carregar tarefas em um arquivo (para persistência).

  • Crie o arquivo utils.py.
  • Crie a função salvar_tarefas(lista_tarefas, nome_arquivo="tarefas.json"):
    • Usa o módulo json (você precisará import json) para salvar a lista_tarefas em um arquivo JSON.
  • Crie a função carregar_tarefas(nome_arquivo="tarefas.json"):
    • Usa o módulo json para carregar tarefas de um arquivo JSON.
    • Lide com o caso de o arquivo não existir (retorne uma lista vazia).
  • (Opcional) Adicione um bloco if __name__ == "__main__": para testar as funções deste módulo.
# gerenciador_tarefas/utils.py
 
import json
import os
 
def salvar_tarefas(lista_tarefas, nome_arquivo="tarefas.json"):
    """Salva a lista de tarefas em um arquivo JSON."""
    try:
        with open(nome_arquivo, 'w', encoding='utf-8') as f:
            json.dump(lista_tarefas, f, indent=4, ensure_ascii=False)
        print(f"Tarefas salvas em '{nome_arquivo}'.")
    except IOError as e:
        print(f"Erro ao salvar tarefas: {e}")
 
def carregar_tarefas(nome_arquivo="tarefas.json"):
    """Carrega a lista de tarefas de um arquivo JSON."""
    if not os.path.exists(nome_arquivo):
        print(f"Arquivo '{nome_arquivo}' não encontrado. Iniciando com lista vazia.")
        return []
    try:
        with open(nome_arquivo, 'r', encoding='utf-8') as f:
            tarefas = json.load(f)
        print(f"Tarefas carregadas de '{nome_arquivo}'.")
        return tarefas
    except json.JSONDecodeError as e:
        print(f"Erro ao decodificar JSON do arquivo '{nome_arquivo}': {e}. Iniciando com lista vazia.")
        return []
    except IOError as e:
        print(f"Erro ao carregar tarefas: {e}. Iniciando com lista vazia.")
        return []
 
if __name__ == "__main__":
    print("--- Testando o módulo utils.py diretamente ---")
    test_tasks = [
        {'id': 1, 'descricao': 'Teste Salvar', 'concluida': False},
        {'id': 2, 'descricao': 'Teste Carregar', 'concluida': True}
    ]
    salvar_tarefas(test_tasks, "test_tasks.json")
    loaded_tasks = carregar_tarefas("test_tasks.json")
    print(f"Tarefas carregadas: {loaded_tasks}")
    # Limpar arquivo de teste
    if os.path.exists("test_tasks.json"):
        os.remove("test_tasks.json")
    print("--- Fim do teste de utils.py ---")
 

🎯 Parte 3: main.py - Interface do Usuário

Este será o script principal que interage com o usuário e coordena os outros módulos.

  • Crie o arquivo main.py.
  • Importe as funções necessárias de tarefas.py e utils.py.
    • Pense em qual tipo de import faz mais sentido aqui (import modulo ou from modulo import funcao).
  • No início do main.py, chame carregar_tarefas para carregar as tarefas existentes. Você precisará atualizar a lista lista_de_tarefas e proximo_id no módulo tarefas com os dados carregados.
    • Dica: Você pode passar a lista carregada para as funções do módulo tarefas ou, para simplificar neste projeto, modificar a lista global tarefas.lista_de_tarefas e tarefas.proximo_id após o carregamento.
  • Crie um loop principal que exiba um menu para o usuário:
    1. Adicionar Tarefa
    2. Listar Tarefas
    3. Marcar Tarefa como Concluída
    4. Sair
  • Use input() para obter a escolha do usuário.
  • Chame as funções apropriadas dos seus módulos (tarefas e utils) com base na escolha do usuário.
  • Antes de sair, chame salvar_tarefas para persistir as alterações.
  • Certifique-se de que o loop principal esteja dentro de um bloco if __name__ == "__main__":.
# gerenciador_tarefas/main.py
 
import tarefas
import utils
 
def inicializar_dados():
    """Carrega as tarefas e inicializa o proximo_id."""
    carregadas = utils.carregar_tarefas()
    tarefas.lista_de_tarefas.extend(carregadas)
    if carregadas:
        # Encontra o maior ID existente e define o próximo ID
        tarefas.proximo_id = max(t['id'] for t in carregadas) + 1
    print(f"Próximo ID inicializado para: {tarefas.proximo_id}")
 
 
def exibir_menu():
    """Exibe o menu de opções."""
    print("\n--- Gerenciador de Tarefas ---")
    print("1. Adicionar Tarefa")
    print("2. Listar Tarefas")
    print("3. Marcar Tarefa como Concluída")
    print("4. Sair")
    print("----------------------------")
 
def main():
    """Função principal do gerenciador de tarefas."""
    inicializar_dados()
 
    while True:
        exibir_menu()
        escolha = input("Escolha uma opção: ")
 
        if escolha == '1':
            descricao = input("Digite a descrição da tarefa: ")
            tarefas.adicionar_tarefa(descricao)
        elif escolha == '2':
            tarefas.listar_tarefas()
        elif escolha == '3':
            try:
                id_tarefa = int(input("Digite o ID da tarefa a ser marcada como concluída: "))
                tarefas.marcar_concluida(id_tarefa)
            except ValueError:
                print("ID inválido. Por favor, digite um número.")
        elif escolha == '4':
            print("Saindo do gerenciador de tarefas. Salvando dados...")
            utils.salvar_tarefas(tarefas.lista_de_tarefas)
            break
        else:
            print("Opção inválida. Por favor, escolha novamente.")
 
if __name__ == "__main__":
    main()
 

Executando o Projeto

  1. Navegue até a pasta gerenciador_tarefas no seu terminal.
  2. Execute o script principal: python main.py
  3. Teste as funcionalidades: adicione tarefas, liste-as, marque-as como concluídas e saia.
  4. Execute novamente para ver se as tarefas foram salvas e carregadas corretamente!

Este projeto demonstra como a modularização torna seu código mais organizado, fácil de manter e reutilizável. Cada módulo tem uma responsabilidade clara! 🎯


6. Resumo e Próximos Passos 🚀

Parabéns! Você dominou a arte de criar e importar módulos customizados em Python.

O que você aprendeu:

  • Módulos são arquivos .py que contêm código Python para organização e reusabilidade.
  • Você pode importar módulos inteiros (import modulo) e acessar seus conteúdos com modulo.item.
  • Você pode importar itens específicos (from modulo import item1, item2) para usá-los diretamente.
  • Você pode usar apelidos (import modulo as m ou from modulo import item as i) para nomes mais curtos ou para evitar conflitos.
  • O caminho de busca de módulos (sys.path) define onde o Python procura por módulos.
  • A variável especial __name__ permite que você execute código condicionalmente, dependendo se o módulo é o script principal ou está sendo importado.
  • Você aplicou esses conceitos em um projeto prático de gerenciador de tarefas, mostrando como organizar um programa maior em módulos.

Próximos Passos:

  1. Pacotes Python: Módulos são ótimos, mas para projetos maiores, você vai querer organizar seus módulos em pacotes (diretórios com um arquivo __init__.py). Este é o próximo nível de modularização!
  2. Biblioteca Padrão: Explore mais módulos da biblioteca padrão do Python (como os, datetime, json, csv, re para expressões regulares, etc.). Eles são uma mina de ouro de funcionalidades prontas.
  3. Gerenciamento de Pacotes (pip): Aprenda a usar pip para instalar pacotes de terceiros (como requests, numpy, pandas) do PyPI (Python Package Index). Isso abrirá um mundo de possibilidades para seus projetos.

Continue praticando e construindo! A modularização é uma habilidade fundamental para escrever código Python limpo, eficiente e escalável. 💪

© 2025 Escola All Dev. Todos os direitos reservados.

Criando e Importando Módulos Customizados - Fundamentos do Python para Iniciantes | escola.all.dev.br