Blog em português onde relato a minha aprendizagem de Python. Sendo um blog, ele deve ser lido de baixo para cima e é escrito orientado a uma audiência com alguns conhecimentos de programação. Não tentando ser uma fonte exaustiva de conhecimento sobre Python, pode, no entanto, servir como crash course a esta linguagem.

Thursday, August 20, 2009

docstrings

Noutras linguagens a documentação de uma função é algo que apenas existe em compile-time, não sendo, em geral. possível consultar ou mesmo manipular essa documentação em runtime. Em python, seguindo a filosofia de que tudo é um objecto de 1ª ordem e reificado, existe uma forma especial para definir a documentação para uma função.
Se a primeira cláusula do corpo de uma função for uma string, essa string é associada ao atributo __doc__ da função e fica presente para ser lida ou escrita em runtime. Chama-se a esta string uma docstring, de Documentation String.
Note-se que me referi ao atributo de uma função. Uma função é um objecto como outro qualquer e, como tal, pode ter atributos.

Um exemplo*:
def x_intercept(m, b):
    """
    Return the zero of the line y=m*x+b.  The zero of a
    line is the point at which it crosses the x axis (y=0).
    """
    return -b/m
Um exemplo útil para as docstrings está no seu uso por parte da função 
standard help que imprime informação sobre o seu argumento. Assim:
>>> help(x_intercept)
Help on function x_intercept in module __main__:

x_intercept(m, b)
    Return the zero of the line y=m*x+b.  The zero of a
    line is the point at which it crosses the x axis (y=0).

>>>
As strings criadas com 3 " mantém a sua formatação (quebras de linha, 
espaços e tabs).
*retirado de http://epydoc.sourceforge.net/docstrings.html

Tuesday, August 11, 2009

Asserções

Sou um grande apologista da construção assert nas várias linguagens. Considero que é essencial e quase obrigatório fazer asserções para forçar que os diferentes componentes são utilizados de forma correcta (validar parâmetros, pré e pós condições de funções, etc.).
Em Python é possível fazer asserções com a construção assert condition [ , expression ]
O aparecimento de uma instrução assert implica que condition seja avaliada e, em caso de ser falsa, é lançada uma instância de AssertionError com o argumento expression (se existir). Repare-se que isto é um mecanismo mais potente do que aquele existente, por exemplo, em C pois é possível controlar o comportamento do programa (quando um assert falha), pois é uma excepção que pode ser apanhada e que não implica necessáriamente que o programa termine.

De notar também que este mecanismo apenas existe quando o código é executado em modo de debug (quando a flag -O não foi passada à máquina virtual).

Sunday, August 2, 2009

Excepções

Python também suporta excepções*. As excepções em Python são bastante usuais de outras linguagens mas têm algumas características especiais.
Começemos pela sua sintaxe:

try:
  #statements1
except [expression [ , target] ]:
  #statements2
[else:
  #statements3]

A semântica é a usual de outras linguagens excepto aqui a keyword catch for substituída por except. O corpo de cada exception handler é executado apenas se expression na cláusula except correspondente estiver de acordo com o objecto excepção que está a ser propagado. Desta forma, expression deve avaliar sempre para uma classe ou tuplo de classes. O target opcional corresponde ao nome que é ligado ao objecto excepção para que este possa ser manipulado dentro do corpo do exception handler.
Exemplo:

try:
  1/0
except ZeroDivisionError:
  print 'caught divide by 0 attempt'

Podemos ter um except sem expressão e, nesse caso, esse handler é executado para qualquer tipo de excepção.
Tal como nos while's e for's, o corpo da cláusula opcional else é  executada apenas quando o corpo dentro do try não lança qualquer tipo de excepção e quando a sua execução não termina por break, return ou continue.


Existe um outro tipo de construção sintáctica conhecida como try/finally e a sua sintaxe é a seguinte:
try:
  #statements
finally:
  #statements
(não pode ter else)
O corpo do finally é sempre executado depois do corpo do try independentemente de este lançar, ou não, excepções. Note-se que até à versão 2.5 do Python as duas construções sintáticas existiam e não podiam ser misturadas, i.e. esta última variante não podia ter else ou excepts. Felizmente, a partir da versão 2.5 existe apenas uma construção na forma:
try:
  ...
except:
  exception handler

finally:
 clean-up code

Quanto à hierarquia de tipos de excepções, penso que não é necessário divulgar aqui mas posso dizer que é bastante complexa e completa.

Por último, para lançar explícitamente uma excepção deve-se usar a keyword raise.
Usar raise sem mais nada permite relançar a mesma excepção que fez com que o fluxo de execução fosse parar ao exception handler. Assim, esta construção apenas faz sentido dentro do corpo de um exception handler ou dentro de uma função chamada por um exception handler.
No caso  geral, a sintaxe é a seguinte:
raise [expressao1 [ , expressao2 ] ]
expressao1 pode avaliar para uma classe, caso onde expressao2 se presente é usada como argumento para construir uma instância da classe cujo valor é expressao1. No entanto, expressao1 pode também ser uma instância de uma qualquer classe e, nesse caso, expressão2 não pode estar presente, sendo essa instância lançada como excepção. Exemplo:
def crossProduct(seq1, seq2):
  if not seq1 or not seq2:
    raise ValueError, "Sequence arguments must be non-empty"
  return [(x1, x2) for x1 in seq1 for x2 in seq2]



*Acho que teria menos trabalho se escrevesse um blog sobre aquilo que o python não suporta.