associação pythonbrasil[11] django zope/plone planet Início Logado como (Entrar)

Revisão 2e 2005-12-22 16:23:54

Excluir mensagem

ModulosPacotes

Módulos e Pacotes

A modularização de uma aplicação é algo extremamente importante. Ela deve ser feita por duas razões:

  • Para escrever módulos e reutilizar o código escrito em outras aplicações;
  • Queremos que o código de nossa aplicação esteja organizado de modo a facilitar o controle e o entendimento do todo, facilitando assim a expansão organizada e a manutenção.

Módulos

O que são?

Em poucas palavras: módulos são arquivos de código Python cuja interface é conhecida e que podem ser importados por outros módulos (daremos uma definição formal mais tarde). Dizer que a "interface é conhecida" siginifica que quando um programador importar um módulo, ele saberá (ou tem meios de saber) quais funções e classes o módulo possui. Ele também saberá como usá-las, isto é, conhecendo seus nomes, parâmetros, que excessões pretende tratar, dentre outras características.

Como funcionam?

Para começar, vamos criar um módulo simples:

   1 # -*- iso:8859-1 -*-
   2 # strformat.py
   3 """Módulo de formatação de strings.
   4 """
   5 
   6 def frmt_bytes(bytes):
   7     """Formata um inteiro enviado em "bytes" para um
   8     forma mais bonitinha, GB, MB, enfim. [1]
   9     """
  10     if bytes < 1024:
  11         return '%dB' % (bytes)
  12     elif bytes < (1024 * 1024):
  13         return '%.1fKB' % (bytes / 1024.0)
  14     elif bytes < (1024 * 1024 * 1024):
  15         return '%.1fMB' % (bytes / 1024.0 / 1024.0)
  16     else:
  17         return '%.1fGB' % (bytes / 1024.0 / 1024.0 / 1024.0)
  18 
  19 def strip_html(text):
  20     """Remove todo o html de uma determinada string. [2]
  21     """
  22     import re
  23     s = re.sub('<[^>]*>', '', text)
  24     return s
  25 
  26 # Código de inicialização:
  27 print "Inicializando módulo strformat"

Primeiro vamos ao caso mais simples. Vamos considerar que "strformat.py" está na mesma pasta que o módulo que irá importá-lo. Assim:

   1 # -*- iso-8859-1 -*-
   2 """Módulo principal.
   3 """
   4 
   5 import strformat
   6 
   7 def main():
   8     """Função principal da aplicação.
   9     """
  10     print strformat.frmt_bytes(502356)
  11     print strformat.strip_html("<b>Texto</b>")
  12 
  13 if __name__ == "__main__":
  14     main()

Agora vamos explicar as linhas de código aonde podem surgir dúvidas.

Na linha 1 de ambos os arquivos o "-*- iso:8859-1 -*-" significa que estamos especificando explicitamente qual a codificação de caracteres utilizada em nosso módulo. Faça a experiência: copie o código do módulo que escrevemos e retire esta linha. O interpretador Python irá exibir um aviso. Isso ocorrerá porque utilizamos caracteres acentuados em nosso comentários e strings, e o interpretador precisa saber qual o código de caracteres usados. Porque ele não adivinha? Porque não existe uma regra padrão de adivinhação. E se houvesse comentários com caracteres japoneses, por exemplo, como o interpretador saberia que deve usar uma determinada codificação (se mesmo humanamente é difícil adivinhar)?

Uma observação sobre idiomas em códigos e comentários: muitos projetos adotam a regra de escrever tudo em inglês. As vantagens disso é que você se livra de ter que especificar codificações e, mais importante, seu código poderá ser entendido por muito mais gente no mundo inteiro. Por outro lado, pode ser que o seu colega de classe não entenda uma linha. O bom é que isso não é uma regra e você é livre para usar o bom senso :-). Mas nunca use acentos em nomes de funções e classes.

Em seguida, vemos as DocStrings do nosso módulo. Este é o modo utilizado para documentarmos o propósito do módulo.

Em seguida, a definição do módulo. Note o modo como usamos DocStrings para os elementos do módulo. Este é o modo correto de documentar, pois tais strings podem ser recuperadas por comandos como o help().

Antes de continuarmos, devemos saber que nosso arquivo principal (o que possui a função main) também é um módulo. Na verdade, todos os arquivos com código Python são módulos, mesmo que não sejam importados. A definição dada inicialmente serve na aplicação deste documento, afinal esta é uma de suas principais utilidades. Então vamos à definição formal:

Um módulo é um arquivo Python contendo definições e sentenças. [3]

Tabela de Símbolos e Namespaces

Note em nosso módulo principal o modo como chamamos as funções de strformat. O detalhe é que nós especificamos o nome do módulo para chamar a função. Para entender porque isso é feito, devemos entender que todo módulo Python possui uma tabela de símbolos.

Uma tabela de símbolos é um dicionário de dados que cada módulo possui, onde são armazenadas todas as variáveis, funções e classes definidas neste módulo.

Vamos a uma pequena demonstração prática. Abra o interpretador de comandos. Se chamarmos a função dir(), o interpretador nos retornará uma lista de nomes de todos os símbolos da tabela do módulo atual. Assim:

   1 >>> dir()
   2 ['__builtins__', '__doc__', '__name__']

Quando inserimos um comando import, o que estamos fazendo é simplesmente adicionar um novo símbolo à nossa tabela. Vamos testar:

   1 >>> import math
   2 >>> dir()
   3 ['__builtins__', '__doc__', '__name__', 'math']
   4 >>> type(math) # Investigando o símbolo
   5 <type 'module'>

Sabemos que math possui a função sqrt(), que calcula a raiz quadrada de um número. Se quisermos chamar srqt(), ele deve estar em nossa tabela de símbolos. Caso contrário, o interpretador Python não irá saber aonde procurá-la. Neste caso, por exemplo, srqt() não está. Mas sabemos que ele está presente na tabela de símbolos de math. Como sabemos? Podemos usar dir(módulo):

   1 >>> dir(math)
   2 ['__doc__', '__file__', '__name__', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'cosh', 'degrees', 'e', 'exp', 'fabs', 'floor', 'fmod', 'frexp', 'hypot', 'ldexp', 'log', 'log10', 'modf', 'pi', 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh']

Felizmente Python permite chamarmos diretamente os símbolos da tabela math. Basta fazermos:

   1 >>> math.sqrt(9)
   2 3.0

Agora podemos entender porque fizemos print strformat.frmt_bytes(502356).

E os namespaces? Esse é outro nome muito encontrado para a tabela de símbolos de um módulo atual. Por exemplo, podemos dizer que srqt() pertence ao namespace de math.

Agora vamos explorar outra alternativa. Python também permite importar diretamente os símbolos de outro namespace para o atual. Vamos reiniciar o interpretador (para limpar a tabela de símbolos) e fazer um novo teste:

   1 >>> dir()
   2 ['__builtins__', '__doc__', '__name__'] # Tabela inicial.
   3 >>> from math import sqrt
   4 >>> dir()
   5 ['__builtins__', '__doc__', '__name__', 'sqrt'] # Novo símbolo importado.

Veja agora que utilizamos o comando na forma "do módulo math, importe o símbolo srqt". É muito importante ressaltar que nós NÃO importamos math. O símbolo math não está na tabela atual. Apenas importamos a função sqrt. Como a função está na tabela atual, podemos fazer:

   1 >>> sqrt(25)
   2 5.0

Mas não podemos fazer:

   1 >>> math.sqrt(25)
   2 
   3 Traceback (most recent call last):
   4   File "<pyshell#15>", line 1, in -toplevel-
   5     math.sqrt(25)
   6 NameError: name 'math' is not defined

Se quisermos utilizar o módulo math, podemos importá-lo normalmente como no exemplo anterior.

   1 >>> import math
   2 >>> dir()
   3 ['__builtins__', '__doc__', '__name__', 'math', 'sqrt']

Os símbolos math e sqrt podem coexistir na mesma tabela sem nenhum problema. A única coisa que não pode ocorrer, por razões óbvias, são dois nomes iguais na mesma tabela.

Também podemos importar diversos símbolos de uma única vez:

   1 >>> dir()
   2 ['__builtins__', '__doc__', '__name__'] # Tabela inicial.
   3 >>> from math import sin, cos
   4 >>> dir()
   5 ['__builtins__', '__doc__', '__name__', 'cos', 'sin']

Há um terceiro modo de se usar import. Podemos importar diretamente todos os símbolos de um módulo. Vamos reiniciar novamente o interpretador e testar:

   1 >>> dir()
   2 ['__builtins__', '__doc__', '__name__'] # Tabela inicial.
   3 >>> from math import * # Importamos tudo.
   4 >>> dir()
   5 ['__builtins__', '__doc__', '__name__', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'cosh', 'degrees', 'e', 'exp', 'fabs', 'floor', 'fmod', 'frexp', 'hypot', 'ldexp', 'log', 'log10', 'modf', 'pi', 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh'] # Os símbolos de math estão aqui agora.

Novamente, nós NÃO importamos math. Apenas todos os símbolos de math, EXCETO os nomes que começam com um _ (sublinhado). O símbolo _ é utilizado para que você possa definir nomes internos que não devem ser exportados para outros módulos.

Código de inicialização

Em nossos módulos, podemos definir código que será executado automaticamente quando forem importados. Se você executar o programa do primeiro exemplo, será exibida a mensagem "Inicializando módulo strformat", que definimos em strformat.

A regra é simples: todo código que estiver definido em primeiro nível, isto é, fora da definição de classes e funções será executado como código de inicialização do módulo.

Entretanto, existem algumas situações aonde queremos que nosso código seja executado apenas sob condições especiais. É o caso dos módulos principais. Sò queremos que nossa função main() seja executada se o módulo for o principal. Caso ele tenha sido importado, a aplicação só deverá ser executada se main() for chamado explicitamente. Para isso, utilizamos o seguinte código:

   1 if __name__ == "__main__":
   2     main()

A variável __name__ armazena o nome do módulo atual. Neste caso, o código de inicialização investiga através dela se o módulo é o principal, e executa de acordo. Você encontrará este código muito frequentemente. Apesar de parecer um artifício, este é o modo correto de se fazer módulos principais.

Pacotes

Quando nossos módulos ficarem maiores, não vamos querer ter cinquenta classes e trezentas funções em um único arquivo. Vamos querer separar em diversos módulos. É para isso que pacotes existem.

O que são?

Pacotes são módulos Python que podem conter outros pacotes. Em termos de armazenamento, enquanto módulos são estruturados em arquivos, pacotes são estruturados em pastas.

Como funcionam?

Breve

O PythonPath

Breve

Referências

[1] http://pythonbrasil.com.br/moin.cgi/FrmtBytes

[2] http://pythonbrasil.com.br/moin.cgi/StripHtml

[3] http://docs.python.org/tut/node8.html