domingo, 13 de outubro de 2013

De que falamos quando falamos de... (2)

Quando escrevemos um programa em Python, seja num vulgar editor de texto seja recorrendo ao editor de um ambiente integrado de desenvolvimento como o Wingware, foi-vos dito para colocar o código
if __name__ == ‘__main__’:
a separar as nossas definições da parte em que as usamos. Por exemplo:
import turtle

def  poligono(posx,posy,orientacao,lado, num_lados):
    """ Desenha um poligono de num_lados ."""
    # Prepara
    turtle.penup()
    turtle.goto(posx,posy)
    turtle.pendown()
    turtle.setheading(orientacao)
    # Desenha
    for i in range(num_lados):
        turtle.forward(lado)
        turtle.left(360/num_lados)
    turtle.hideturtle()

if __name__ == ‘__main__’:
    poligono(-50,-50,0,20,8)
    turtle.exitonclick()
Será que é obrigatório fazer assim? Não, claro que não. Já vimos que podemos não usar a construção condicional if no final e o nosso programa corre na mesma. Então porque usamos?? Vamos tentar explicar. Sabemos que a linguagem Python pode ser estendida, adicionando novos comandos guardados em módulos. Esses módulos, para podermos usar as constantes e comando que nele estão definidos, têm que ser importados. Os módulos são tecnicamente objectos e, como todos os objectos têm um conjunto de atributos:
>>> import math
>>> id(math)
4528111056
>>> math

>>> type(math)
< class 'module' >
>>>
Isso mesmo, têm identidade, valor e tipo. Mas têm outros atributos igualmente que podem ser identificados consultando o módulo.
>>> dir(math)
['__doc__', '__file__', '__name__', '__package__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'hypot', 'isfinite', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'modf', 'pi', 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'trunc']
>>>
Um dos atributos é o nome (__name__). Quando importamos um módulo é criado um espaço de nomes e __name__ fica associado a um objecto do tipo cadeia de caracteres que define o nome do módulo, após a importação. E podemos saber qual é consultando como habitualmente qual o valor do objecto associado ao nome.
>>> math.__name__
'math'
>>> 
Quando importamos o nome do módulo é igual ao nome do ficheiro que implementa o módulo depois de retirada a extensão do ficheiro. Regressemos agora aos nosso ficheiros com código Python. São criados com a extensão ‘.py’. Também eles têm a natureza de ... módulos. Podem por isso ser importados, e têm os mesmos atributos:
>>> import poli
>>> id(poli)
4546041256
>>> poli

>>> type(poli)
< class 'module' >
>>> dir(poli)
['__builtins__', '__cached__', '__doc__', '__file__', '__name__', '__package__', 'poligono', 'turtle']
>>> poli.__name__
'poli'
>>>
Uma vez mais um ficheiro com código Python escrito por nós quando importado tem um nome igual ao nome do ficheiro depois de retirada a extensão. Olhando para o código do ficheiro indicado acima, o leitor atento terá verificado que a chamada à definição polígono e ao método exitonclick() não ocorreram. E porquê? Por que serem executadas é preciso que o teste.
if __name__ == ‘__main__’:
seja verdadeiro, o que não acontece pois, como acabámos de ver, quando um módulo/ficheiro é importado o seu nome é igual ao do módulo! Vejamos agora o que acontece quando em vez de importar executamos o módulo/ficheiro.
Agora sim, aparece o polígono ! Conclusão. O recurso à separação do nosso ficheiro em duas partes, uma onde definimos o código e a outra onde executamos condicionalmente algumas instruções, permite que um ficheiro possa ter dois comportamentos diferentes, quando importamos ou quando mandamos correr/executar o ficheiro. Mas porque é que podemos querer este duplo comportamento? Bem, se o nosso programa for apenas para ser executado por nós e nunca importado não precisamos. Mas se por acaso nós, ou alguém, quiser usar algumas das definições que estão no nosso ficheiro para fins diversos dos nossos então precisamos de usar a instrução condicional a separar as duas partes: definição e uso. Refira-se ainda que muitos programadores que desenvolvem módulos complexos para Python usam esta facilidade para colocar a seguir ao if um conjunto de chamadas às definições que ilustram o que fazem as diferentes instruções contidas na primeira parte do módulo. Se ainda tem duvidas e quer testar com um código minimalista então crie um ficheiro com o seguinte conteúdo.
""" name_main.py."""

if __name__ == '__main__':
 print('Execução')
else:
 print('Importação')
salve-o com um nome apropriado (por exemplo name_main.py), e teste em duas situações: importe o módulo e execute o programa.

Sem comentários:

Enviar um comentário