Aula 12: Módulos em Python

Aulas a distância - Google Meet:

Para verem os vídeos, vocês devem estar logados no e-mail da usp.

Motivação:

Ao sair e entrar de novo no interpretador Python, as definições anteriores (funções e variáveis) são perdidas. Portanto, se quiser escrever um programa maior, será mais eficiente usar um editor de texto para preparar as entradas para o interpretador, e executá-lo usando o arquivo como entrada. Isso é conhecido como criar um script. Se o programa se torna ainda maior, é uma boa prática dividi-lo em arquivos menores, para facilitar a manutenção. Também é preferível usar um arquivo separado para uma função que você escreveria em vários programas diferentes, para não copiar a definição de função em cada um deles.

Para permitir isso, o Python tem uma maneira de colocar as definições em um arquivo e então usá-las em um script ou em uma execução interativa do interpretador. Tal arquivo é chamado de módulo; definições de um módulo podem ser importadas por outros módulos, por um script ou diretamente no Python Shell.

Um módulo é um arquivo contendo definições e instruções Python. O nome do arquivo é o nome do módulo acrescido do sufixo .py. Por exemplo, use seu editor de texto favorito para criar um arquivo chamado ponto.py no diretório atual com o seguinte conteúdo:

import math

def distancia(Pa, Pb):
    """ Calcula a distância entre dois pontos Pa = [xa,ya] e Pb = [xb,yb]"""
    xa,ya = Pa #xa,ya = Pa[0],Pa[1]
    xb,yb = Pb #xa,ya = Pb[0],Pb[1]
    dx = xb-xa
    dy = yb-ya
    d = math.sqrt(dx*dx + dy*dy)
    return d
  
Considerando um ponto como uma lista com dois elementos reais, correspondendo às coordenadas x e y, o módulo ponto acima define uma função para calcular a distância euclidiana entre dois pontos fornecidos. Para calcular a raiz quadrada, usamos a função sqrt do módulo math do Python, usando a sintaxe math.sqrt.

Agora, crie um segundo arquivo chamado poligono.py no mesmo diretório com o seguinte conteúdo:

import ponto

def perimetro(Pol):
    """Calcula o perímetro de um polígono Pol = [P1, P2, ..., Pn] composto por n pontos."""
    peri = 0.0 #perímetro
    Pant = Pol[-1] #ponto anterior
    for Pi in Pol:
        dist = ponto.distancia(Pant, Pi)
        peri += dist
        Pant = Pi
    return peri
  
Considerando um polígono como uma lista dos seus vértices consecutivos, o módulo poligono acima define uma função para calcular o perímetro de um polígono fornecido, ou seja, a soma de todos os seus lados. Porém, como cada vértice é o ponto comum entre os lados do polígono, podemos calcular o tamanho de cada lado usando a função distancia do módulo ponto. Para isso, precisamos primeiramente importar o módulo ponto, usando o comando import ponto. Feito isso, podemos então chamar a função distancia usando a sintaxe ponto.distancia.

Agora entre no interpretador Python e importe o módulo com o seguinte comando:

>>> import poligono

Nota: O caminho de busca dos módulos.

Quando um módulo chamado poligono é importado, o interpretador procura um módulo embutido com este nome. Se não encontra, procura um arquivo chamado poligono.py em uma lista de diretórios incluídos na variável sys.path.
>>> import sys
>>> print(sys.path)
['', '/home/pmiranda', '/usr/bin', '/usr/lib/python35.zip', '/usr/lib/python3.5', '/usr/lib/python3.5/plat-x86_64-linux-gnu', '/usr/lib/python3.5/lib-dynload', '/home/pmiranda/.local/lib/python3.5/site-packages', '/usr/local/lib/python3.5/dist-packages', '/usr/lib/python3/dist-packages']

Se o comando import poligono não estiver funcionando, você deve incluir o diretório que contém os arquivos poligono.py e ponto.py na lista sys.path.
No meu caso os arquivos estão no diretório do Linux /home/pmiranda/Desktop/MAC2166_1s20/modulos. Logo, podemos usar:

>>> sys.path.append('/home/pmiranda/Desktop/MAC2166_1s20/modulos')

No caso do sistema operacional Windows, a estrutura das pastas será diferente e você deve usar um comando similar a este: sys.path.append('C:\\user\\mydir')

O comando import poligono não coloca os nomes das funções definidas em poligono diretamente na tabela de símbolos atual; isso coloca somente o nome do módulo poligono. Usando o nome do módulo você pode acessar as funções através da sintaxe nome_do_módulo.nome_da_função:

>>> poligono.perimetro([[0,0],[1,0],[1,1],[0,1]])
4.0

Se pretender usar uma função muitas vezes, você pode atribui-lá a um nome local:

>>> perimetro = poligono.perimetro
>>> perimetro([[0,0],[1,0],[1,1],[0,1]])
4.0

Nota:

Por motivos de eficiência, cada módulo é importado apenas uma vez por sessão do interpretador. Portanto, se você alterar seus módulos, reinicie o interpretador - ou, se for apenas um módulo que deseja testar interativamente, use importlib.reload() para recarregar o módulo:
>>> import importlib
>>> importlib.reload(poligono)

Dentro de um módulo, o nome do módulo (como uma string) está disponível como o valor da variável global __name__.

>>> poligono.__name__
'poligono'

Por exemplo, se você adicionar o código print(__name__) ao final do arquivo poligono.py, ao importar esse módulo em um Python Shell recém-aberto teremos:

>>> import poligono
poligono

No entanto, quando você roda um módulo Python no modo script (clicando em "Run Module" tal como indicado na figura abaixo), o código no módulo será executado, da mesma forma que quando é importado, mas com a variável __name__ com valor "__main__", ao invés de valer a string do nome do módulo "poligono".

matriz

Ou seja, no Python Shell teremos como saída:

matriz

Isto significa que adicionando este código abaixo ao final do módulo poligono (no lugar do print(__name__)):

if __name__ == "__main__":
    per = perimetro([[0,0],[1,0],[1,1],[0,1]])
    print("Perímetro:",per)
  
você pode tornar o arquivo utilizável tanto como script quanto como um módulo importável, porque o código acima só roda se o módulo é executado como arquivo "principal". Se o módulo é importado, o código não é executado.

Ou seja, se rodarmos agora o módulo poligono no modo script, teremos como saída:

matriz

Isso é frequentemente usado para fornecer uma interface de usuário conveniente para um módulo, ou para realizar testes (rodando o módulo como um script executa um conjunto de testes) para verificar a integridade do módulo.