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.

Saturday, September 5, 2009

Pacotes

Pacotes são uma forma de estruturar conjuntos de módulos de uma forma lógica e útil.

Suponha que quer construir um conjunto de módulos para lidar de forma transparente com vários tipos de imagens. Ler e escrever vários tipos de formatos bem como conseguir aplicar diferentes tipos de efeitos gráficos. Uma possível estrutura para os diferentes módulos é a seguinte:

Imagens/
  formatos/
    jpgread.py
    jpgwrite.py
    bmpread.py
    ...
  efeitos/
    blur.py
    sharpen.py
    ...

Em python aquilo que transforma um conjunto de módulos e directorias num pacote é a presença de um ficheiro chamado __init__.py  na directoria de topo. Este ficheiro é executado quando o pacote ou algum módulo do pacote é importado e, na sua forma mais simples pode ser um ficheiro vazio. A partir de Python 2.5, uma subdirectoria de um pacote é automáticamente um pacote mesmo que esta não contenha o seu __init__.py e esta regra é válida para todas as profundidades.

 Módulos complexos devem usar o ficheiro __init__.py para inicializações. Assim, ficamos com a seguinte hierarquia:

Imagens/
  __init__.py

  formatos/
    jpgread.py
    jpgwrite.py
    bmpread.py
    ...
  efeitos/
    blur.py
    sharpen.py
    ...

cujos  módulos podem ser importados de forma parecida ao Java. Exemplo:
import Imagens.efeitos.blur
cujas funções teriam que ser referenciadas com o nome completo. Para contornar este "problema" podemos também usar a forma from Imagens.efeitos import blur cuja semântica é exactamente igual que quando um módulo não pertencente a um pacote é importado.


Módulos podem importar-se entre si usando caminhos absolutos (a partir de imagens) ou mesmo caminhos relativos.


Por último, os pacotes podem residir no sistema de ficheiros (numa qualquer directoria referenciada por PYTHONPATH) ou ainda num ficheiro do tipo zip de forma transparente.

Wednesday, September 2, 2009

Módulos

É possível definir módulos em python. Qualquer ficheiro com código pode ser um módulo. Para importar um módulo deve-se escrever import moduleName.
Ao importar um módulo, as suas definições não são inseridas no ambiente actual, pelo contrário, apenas o nome do módulo é inserido no ambiente importador.
Vejamos exemplos (tirados de http://docs.python.org/tutorial/modules.html).

Considere-se o ficheiro fibo com o seguinte conteúdo.
# Fibonacci numbers module

def fib(n):    # write Fibonacci series up to n
    a, b = 0, 1
    while b < n:
        print b,
        a, b = b, a+b

def fib2(n): # return Fibonacci series up to n
    result = []
    a, b = 0, 1
    while b < n:
        result.append(b)
        a, b = b, a+b
    return result
 
Ao fazer import fibo, o ficheiro acima é lido, compilado no momento e o 
nome fibo fica associado (no ambiente importador) ao objecto do tipo Module.
 
Se se quiser que as funcoes e variáveis definidas num módulo fiquem 
visíveis no módulo importador deve-se usar a construção
from module import name1, name2, ...
 
No exemplo anterior, se o objectivo fosse o de inserir as funções fib e fib2
no ambiente importador, poderíamos escrever:
from fibo import fib, fib2
ou até 
from fibo import *
Não aconselho esta última variante pois pode redefinir de uma forma
pouco clara determinadas funções no ambiente importador.
Note-se ainda que estas variantes não introduzem o nome fibo no ambiente.
Tal como em Java, existe um conjunto de directorias onde são procurados
os módulos quando importados. Primeiro na directoria actual e depois, por 
ordem, nas directorias definidas na variável de ambiente PYTHONPATH e por último
numa directoria defeito (normalmente onde o Python está instalado).
Como cada ficheiro pode potencialmente ser visto como um módulo, existe uma forma comum para o código distinguir se está a ser corrido ou importado.
Quando um módulo é executado, o seu atributo __name__ é associado a '__main__'. Quando é importado não é, Assim, é muito comum ver construções do tipo:
if __name__ == '__main__':
  #code executed when this file is executed like a script.
 
Considera-se boa práctica que um módulo puro quando executado directamente
corra testes de integridade ou sirva alguma funcionalidade ao utilizador.
Assim, por exemplo o módulo fibo anterior poderia ter:

if __name__ == "__main__":
    import sys
    fib(int(sys.argv[1]))
e portanto poderia ser importado e usado por outros programas ou ser usado
directamente como um programa que calcula o fibonacci do seu primeiro
argumento.


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.

Saturday, July 18, 2009

Um aparte

Não usem o blogger para blogs técnicos onde querem meter código. Ainda não consegui arranjar uma forma fácil de garantir que a formatação e identação se mantém intacta. Enfim... agora é tarde e, como tal, peço alguma paciência para com os problemas de formatação deste blog.
Obrigado

Thursday, July 16, 2009

Métodos estáticos e Decorators

No último post sobre programação orientada a objectos esequeci-me de referir como declarar métodos static.
Para declarar métodos static, deve-se usar a função built-in staticmethod e deve ser usado da seguinte forma:
class AClass(object):
  def someStaticMethod():
     print 'i am static'
  someStaticMethod = staticmethod(someStaticMethod)

Na realidade, a única diferença entre um método static e um "normal" é que o static não recebe como primeiro argumento o valor do self. Como noutras linguagens, o método static pode ser chamado sobre uma instância dessa classe ou sobre a sua classe.

No entanto, esta forma de definir métodos não é muito simpática. Para resolver isso, o Python suporta um mecanismo adicional, decorators, que irei descrever de seguida.
Um decorator é algo que deve ser posto imediatamente antes da definição do método e tem a forma @nomeDoDecorator
de uma forma geral, @expressao avalia a expressao (que tem de ser um nome, ou uma chamada a funcao), associa esse valor a uma variável temporária (chamemos-lhe __aux) e logo a seguir à avaliação do def

que tem de aparecer a seguir ao decorator, a expressão f = __aux(f) é executada, onde f é o nome posto no def.
Este mecanismo permite uma forma muito simples de contornar a verbosidade da definição de métodos estáticos. Assim:
class AClass(object):
  @staticmethod

  def someStaticMethod():
     print 'i am static'
  
No entanto, este mecanismo pode ser usado em qualquer função, seja ela um método de uma classe ou não. É inclusivé possível criar novos decorators (que são simplesmente funções de ordem superior).
Veja-se um exemplo onde se cria um decorator que faz com que a docstring associda a uma função seja impressa no momento da sua definição. (Exemplo de Python in a Nutshell - Alex Martelli)
def showdoc(f):
  if f.__doc__:
    print '%s: %s' % (f.__name__, f.__doc__)
  else
    print '%s: No docstring!' % f.__name__
  return f




@showdoc
def f1: "a docstring"


@docstring

def f2(): pass

 Quando f1 é definido, a string "f1: a docstring" é impressa. Quando f2 é definido "f2: No docstring!" é impresso.

Wednesday, July 8, 2009

list comprehensions e generator expressions

Como se viu na criação de listas e dicionários, Python tem formas muito sucintas de criar objectos complexos.
Qualquer programador sabe que é muito comum criar listas cujos elementos são computados no momento. Normalmente usa-se uma qualquer forma de iteração, onde o elemento é primeiro calculado e depois posto dentro da lista. Python suporta esta funcionalidade de uma forma muito simpática, chamada de list comprehensions.

list comprehensions
Uma list comprehension é uma expressão e tem a seguinte sintaxe:


[expression for target in iterable lc-clauses ]

target e expressao têm a mesma semântica que no for.
lc-clauses consiste numa sequência de 0 ou + clausulas, cada uma com a seguinte sintaxe:
for target in iterable
OU
if expression

Exemplos[1] (a lista resultante será posta numa variável para maior claridade):
result = [x+1 for x in someSequence]
significa exactamente o mesmo que:
result = []
for x in someSequence:
  result.append(x+1)

i.e. Para cada elemento de someSequence, este elemento é associado a target,(neste caso x), e expression é avaliada (neste caso x+1) e o seu resultado posto numa lista nova.

Penso que as list comprehensions são uma forma muito expressiva de executar idiomas muito comuns. Vejamos mais exemplos:


result = [x+1 for x in someSequence if x>23]
aqui, depois de x ser associado ao valor vindo de someSequence, a expressão dentro do if é avaliada e se for True então o resultado da avaliação de expression é posto na lista.


Um último exemplo, neste caso usando uma lc-clause na forma de for:
result = [x+y for x in someSequence for y in another]
o que é equivalente a:
result = []
for x in someSequence:
  for y in another:
    result.append(x+y)


[1] - Exemplos retirados de Python in a nutshell - Alex Martelli

Tuesday, June 30, 2009

Lambdas e geradores

A expressão lambda cria uma função sem associar-lhe qualquer nome. Assim, a expressão
lambda parametros : expressão

constroi uma função com código equivalente a:
def anonimo(parametros):
  return expressao

excepto pelo facto de alterar a ligacao ao nome anonimo

Exemplos:
>>> quadrado=(lambda x : x*x)
>>> quadrado
at 0x028DA6B0>
>>> quadrado(3)
9
>>>



Geradores (Generators):
Um gerador é qualquer função que contenha pelo menos uma keywork yield. Ao chamar uma função deste tipo, o corpo da função não é executada. Pelo contrário, ao chamar um gerador, é criado um iterador especial que contêm a função, as suas variáveis locais (e parâmetros) e o ponto actual de execução, que é inicialmente definido como o início da função. Ao chamar o método next deste gerador, a execução da função retoma do ponto actual até ao primeiro yield. De uma forma muito práctica, um yield é como um return pois permite retornar um valor para o contexto mas quando a sua execução retoma esta é retomada a partir do yield em vez do início da função. Exemplo:
Imagine-se que queremos uma sequência de números de 1 a N e depois até 1 de novo. (Tirado de Python in a nutshell - Alex Martelli):
def updown(n):
  for x in xrange(1, n): 
    yield x
  for x in xrange(n, 0, -1):
    yield x

Exemplo de utilização:
for i in updown(3): print i
imprime 1,2,3,2,1

Programação orientada a objectos em Python

Em Python uma classe é definida com a keyword class. Exemplo: 
 
class Basket:

    # Always remember the *self* argument
    def __init__(self,contents=None):
        self.contents = contents or [] 
        #esta funcao nunca pode retornar nada 

     def add(self,element):
        self.contents.append(element)

     def print_me(self):
        result = ""
        for element in self.contents:
            result = result + " " + `element`
        print "Contains:"+result

Novos conceitos:
  1. Todos os métodos têm de receber um argumento adicional explícitamente que corresponde ao objecto sobre o qual este método deve ser executado. É normal que esse argumento seja chamado self mas isto não é obrigatório. No entanto tem de ser sempre o primeiro argumento.
  2. Alguns métodos, como __init__ (com dois underscores antes e depois), são prédefinidos (podendo ser redefinidos) e têm significados especiais. __init__ é o nome do construtor da classe e é portanto o método que é chamado quando se escreve algo Basket(args), logo após o espaço para o objecto ser criado.
  3. Para aceder aos campos de uma classe é necessário escrever self.campo, isto porque escrever só campo mesmo dentro do código das funções da classe, seria uma construção ambígua (pelo facto de as variáveis locais não serem declarada).
  4. O método print_me deveria ser chamado __str__ que é o método "especial" implícitamente chamado sempre que uma representação, do objecto, em string é desejada. Assim seria possível chamar print para uma instância de Basket.
  5. A partir de Python 2.5 todas as classes devem herdar de object. Assim, Basket deveria ser class Basket(object). O facto de isto ser explícito é mau porque implica que se escreva um pouco mais em relação a outras linguagens mas, é também mais extensível.

Não existem mecanismos de protecção de variáveis ou métodos (não existem formas de dizer que algo é private ou protected ou mecanismos similares). A encapsulação é deixada a cargo do programador. No entanto, existe a convenção de que métodos e variáveis cujo nome começa com 1 underscore devem ser privados. Na minha opinião este é um dos defeitos do Python pois a linguagem em si não proíbe que se violem as abstracções. Existem outros defeitos mas lá iremos a seu tempo.

Para definir a superclasse de uma classe deve-se usar parentesis a seguir ao nome. Assim:

class SpamBasket(Basket):
#...

Python suporta também herança múltipla. Para dizer que A é um subtipo de 
B e C usa-se:
class A(B, C):
 
É necessário chamar o construtor da superclasse mesmo que não seja 
necessário passar-lhe argumentos especiais. Desta forma, no __init__ 
do SpamBasket é necessário chamar o __init__ da sua superclasse,
passando explícitamente o argumento self:
Basket.__init__(self)

Para tornar isto ainda mais complexo (e confuso), o Python suporta metaclasses.
Metaclasses são, como o nome indica, classes de classes. Não vou aqui discutir
os mecanismos relacionados com as metaclasses pois são um mecanismo
avançado para esta fase (e, honestamente, ainda não tive tempo para os practicar
e perceber os seus pontos fortes e fracos). Mais tarde vou tentar voltar
a este ponto pois acho que as linguagens que suportam metaclasses são 
"potentíssimas", apesar de ser um mecanismo raramente utilizado. Um bom uso
para metaclasses é, por exemplo, a criação de uma metaclasse cujas instâncias
das suas classes, bem como os campos "static" da classe, são implicitamente
tornados permanentes numa base de dados.

Quanto aos campos partilhados por todas as instâncias eles são definidos no corpo da classe:
class Foo(object):
  x=23
  y=x+22


  def method(self):  
    print Foo.x #nao se pode escrever simplesmente print x

Notar que estes campos static são criados e os seus valores definidos quando a construção sintáctica class é avaliada, o que constrói um objecto do tipo class associado ao nome foo, algo que vem da filosofia de que tudo é um objecto de 1ª classe.

Tuesday, June 16, 2009

Qual é a verdade?

Vou falar de Booleanos em Python. Cada linguagem apresenta a sua interpretação do que é verdade. Python não é excepção.
Em python, tudo é verdade (True) excepto: os containers vazios (listas vazias, sets vazios,...), None (o equivalente ao null noutros mundos) e 0. Espero não estar a esquecer casos. Vou usar a função bool() para exemplificar:
>>> bool(0)
False
>>> bool(1)
True
>>> bool([])
False
(lista vazia é interpretada como falsa)
>>> bool([1])
True 
(lista não vazia é interpretada como verdade)
>>> bool([1,2])
True
>>> bool(())
False
(tuplo vazia é interpretada como falso)
>>> bool((None))
False 
(None é falso)
>>> bool((None,))
True 
(Tuplo com None já é True)
>>> bool(False)
False
>>> bool(not False)
True
>>> bool(list)
True  
(Um tipo também é verdadeiro)
>>> list

>>>


A avaliação de expressões booleanas do tipo a and b tem também as suas peculiariedades:
Primeiro ver se a é true. Se não for então o valor da expressão é a. Se for, então o valor da expressão é b.
Isto é chamado de short circuiting e existe em outras linguagens também, mas a peculiarieadade é que, juntamente com o facto de muitas coisas serem True ou False podemos ter construções muito interessantes.
Por exemplo:


if a:
    print a
else:
    print b

pode ser simplesmente escrito como 
 
print a or b
 
o que imprime a ou b consoante a seja interpretado como verdade ou b caso
contrário.
Veja-se:
>>> print [1] or [2]
[1]
>>> print [1] or []
[1]
>>> print [] or [2]
[2]
>>> 

Thursday, June 11, 2009

Listas, dicionários e tipos de argumentos

Python, sendo uma linguagem de alto nível,  suporta nativamente estruturas de dados complexas como listas e dicionários, entre outros.
Listas
Um aspecto muito interessante é que muitos tipos de estruturas de dados complexas têm uma forma sucinta para serem definidas. No caso das listas, estas podem ser criadas com parênteses rectos:
x=[1,2,3]
 , (como aliás já tinha sido mostrado num post anterior.)
Obviamente, as listas podem ser aninhadas:
x=[ [1, 2], 3 ]
(uma lista de dois elementos onde o primeiro elemento é ele próprio uma lista (com os elementos 1 e 2) e o segundo elemento é o 3.
Assim, a avaliação da construção sintáctica [ ... ] cria uma lista (uma instância de list) cujos elementos são dados pela avaliação do que está dentro dos parênteses. Uma lista vazia é naturalmente escrita com [].
Também é possível criar uma lista usando list(). Mais, em python, para criar uma instância de uma classe xpto deve-se escrever xpto(). i.e. ao definir uma classe C, fica implicitamente definida uma função com nome C, função essa que tem todas as propriedades das outras funções e é chamada da mesma forma.

Tuplos
Um tuplo é uma sequência ordenada de elementos cujos tipos podem arbitrários e até diferentes. Tal como para as listas, existem formas sucintas de criar tuplos. Os tuplos criam-se com parêntesis.
Exemplo:
() <- tuplo vazio. Os parêntesis neste caso não são opcionais.
(100, 200, 300) <- tuplo com 3 elementos
(3.14, ) <- forma de criar um tuplo com apenas um elemento. a virgula a seguir ao número NÃO é um engano. Ela existe pois de outra forma seria impossível distinguir da aplicação normal de parêntesis para avaliar algo primeiro. Tal como nas listas, pode-se chamar o construtor de tuplo, que é a função tuple(). Esta função recebe no máximo um argumento e, quando esse argumento é um iterável, o tuplo contem os mesmos elementos do iterável.
Exemplo:
tuple('wow'), retorna um tuplo igual a ('w', 'o', 'w') pois uma string é um tipo de iteravel cujos elementos são os seus caracteres. Para ter um tuplo com 1 único elemento igual a 'wow' deve-se usar ('wow', ) ou então
x = 'ola'
tuplo = ( x, )

Dicionários
Um dicionário é um tipo de container (um objecto que contém outros objectos) que permite mapear uma colecção arbitrária de objectos a uma colecção arbitrária de outros objectos, chamados chaves. Noutros contextos, estas estruturas são também chamadas de mapas ou hash tables.
Estes também possuem um açucar sintáctico para a sua definição:
{'x':42, 'y':3.14, 'z':7}
esta expressão tem como valor um dicionário que mapeia a string 'x' ao elemento 42, a string 'y' ao número 3.14 etc. Para definir um dicionário vazio é, como se espera, {}
Também se pode criar um dicionáriochamando o seu construtor. Irei exemplificar para aproveitar e apresentar outra característica da linguagem:
dict(x=42, y=3.14, z=7)
Esta expressão cria exactamente o mesmo dicionário que a anterior. O que quero apresentar é que, na chamada a uma função o python suporta named arguments i.e. para além dos argumentos posicionais normais, pode-se passar também novos argumentos no formato nome=valor. No caso particular da função dict, ela usa esses argumentos nomeados para criar um dicionário contendo um mapeamento entre os seus nomes ('x','y','z') e os seus valores.

No caso geral, imagine-se a seguinte funcão:
def divide(divisor, dividend):
  return dividend / divisor

Esta função pode ser chamada de várias formas:
divide(20,5) , a chamada "normal" onde os parametros formais são ligados aos valores pela sua posição relativa (20 fica ligado a divisor e 5 fica ligado a dividend).
No entanto, ela também pode ser chamada assim:
divide(dividend=94, divisor=12)

Um uso excelente para os named arguments, é a de passar parâmetros opcionais. Considere-se a seguinte função:
def f(middle, begin='init', end='finis'):
  return begin+middle+end

Esta função apresenta vários outros conceitos. Ao fazer a=b na lista de parâmetros formais, estamos a definir o valor de defeito como sendo b para o argumento com nome a, como em C++.
Segundo, tal como em Java, o + pode ser usado para concatenar strings mas, em python, o mecanismo é bastante mais poderoso, sendo parecido com o de C++.
ao fazer a+b, o Python chama o método __add__ sobre o objecto a com argumento b. Claro que este método pode ser chamado explícitamente:
>>> 'a'+'b'
'ab'
>>> 'a'.__add__('b')
'ab'
>>>


Aproveito também para dizer que practicamente todos os mecanismos "especiais" do python podem ser alterados pois por trás existem sempre chamadas a método especiais. Métodos especiais devem ser chamados __nome__ (são dois underscores antes e depois). Todos os operadores, comparações, conversões implícitas entre tipos são executadas por métodos especiais. A única excepção existente é para a atribuição a=b que não pode ser overloaded e tem sempre a mesma semântica.

Voltando ao exemplo da função f. Esta função pode ser chamada das seguintes formas:
>>> f('ola')
initolafinis
>>> f('ola',end=' mundo')
initola mundo
>>> f('ola',end=' mundo', begin='')
ola mundo
>>>


Voltando ainda aos containers (dicionários e listas etc.), podemos obter os seus elementos usando o operador de indexação []. Alguns exemplos:
>>> listaMaravilha=[1,2,3,4]
>>> listaMaravilha
[1, 2, 3, 4]
>>> listaMaravilha[3]
4

Note-se que as listas sao indexadas a partir do 0'
>>> listaMaravilha[-1]
4
'Mas tambem se pode usar indices negativos. O significado é o de comecar a partir do fim. Veja-se:
>>> listaMaravilha[-2]
3

No entanto, os indíces admissíveis são apenas entre [-N, N+1], onde N é o número de elementos.
>>> listaMaravilha[-5]
Traceback (most recent call last):
  File "", line 1, in
IndexError: list index out of range
>>> listaMaravilha[4]
Traceback (most recent call last):
  File "", line 1, in
IndexError: list index out of range
>>>



Da mesma forma se pode indexar e até alterar os valores de um dicionário.
>>> foo={'ola':1, 'mundo':2}
>>> foo[0]
Traceback (most recent call last):
  File "", line 1, in
KeyError: 0
>>> foo['mundo']
2
>>> foo['mundo']=3
>>> foo['mundo']
3
>>>

>>> foo
{'ola': 1, 'mundo': 3}
>>> foo['nova chave']=43
>>> foo
{'ola': 1, 'nova chave': 43, 'mundo': 3}
>>>

Tuesday, June 2, 2009

Controlo de fluxo

Neste post falo do if,while e for para controlo de fluxo de execução.
Sintaxe do if:

if : bloco
elif : bloco ( pode ter 0 ou + ocorrências)
else : bloco (opcional)

Exemplos:
if x==y:
  print "yes"
else:
  print "no"

Sintaxe do while:

while : bloco
else : bloco

O while pode levar um else opcional. O bloco dentro do else é executado apenas quando while termina normalmente, ie, sem break, excepção ou return. Como em C, pode-se usar as keywords continue, e break com o mesmo significado.

Sintaxe do for:

for name in iteravel:
else : bloco

Este for é mais parecido com o foreach do java do que com o for de C. Como o while, ele pode levar um else e a semântica é a mesma. deve ser uma expressão que quando avaliada produz um objecto iterável. Um iterável é algo que pode ser iterado. Regral geral, um iterable é do tipo Iterator. Um iterator i é um objecto com o método next tal que se pode chamar i.next() para obter o próximo valor do iterador, ou a excepção StopIteration quando não existem mais elementos.
Repare-se que a sintaxe para chamar uma função ou método é exactamente a mesma que em C/Java método(argumentos) e que chamar um método sobre uma instância é feito com o "." , i.e. objecto.método(argumentos)

Assim, este for começa por avaliar a expressão que deve produzir um objecto do tipo Iterator. De seguida, para cada objecto retornado pelo método next() do Iterator, ele atribui esse objecto a e executa o bloco de código.
Exemplo:

# Imprimir todos os valores de 0 até 99 inclusivé.
for value in range(100):
  print value

range é uma função standard que devolve uma lista cujos elementos base
são os valores entre 0 e o argumento passado. Python chama implícitamente iter() sobre
a lista retornada para obter um Iterator sobre os elementos da lista.
Note-se que "range(100)" só é avaliado uma vez. Alterar os elementos da lista
de base do iterador pode ser feito mas tem uma semântica esquisita.
Quando o iterador é criado inicialmente, ele pede à lista o número de elementos
e de seguida itera vai buscar os valores da lista usando um índice incremental.
Assim, se se retirar o elemento da lista correspondente à posição actual
na lista, o próximo elemento não será considerado pelo iterador (ao eliminar o elemento
da lista, todos os indices dos elementos na lista são decrementados, i.e.
a lista é shiftada para a esquerda a partir daquele ponto mas o iterador
vai continuar a ir buscar o mesmo indice).
Exemplo:
def exemploIter():
  y = [2,3,4]
  for x in y:
    print x


  for x in y:
    y.remove(x)
    print x

  print y

Copiando isto para um ficheiro exemplo.py e correndo
python -i exemplo.py
faz com que o ficheiro seja avaliado e de seguida se apresente uma shell
para o interpretador.
Chamando exemploIter, escrevendo exemploIter() obtemos o seguinte:

C:\Users\tiago>python -i py.py
>>> exemploIter()
2
3
4
2
4
[3]
>>>

as primeiros 3 linhas 2,3,4 corresponde ao primeiro for a imprimir os elementos
da lista.
De seguida note-se que apenas 2 e 4 são impressos. Isto ocorre, como expliquei,
porque quando o iterador retorna o primeiro elemento (2), este é associado
a x e depois removido da lista. Assim, o elemento 3 que estava no índice 1
passou a estar no índice 0, mas, o iterador vai de seguida buscar o mesmo
índice (1) que corresponde ao valor 4 e imprime-o e elimina-o da lista.
De facto, o último print imprime a lista y , o que faz com que seja impresso
[3], que é a representação textual de uma lista apenas com o elemento 3.

Thursday, May 21, 2009

Primeiros passos

Algumas notas sobre a linguagem. A partir deste post irei apresentar a linguagem.

Constantes importantes
None semânticamente equivalente a null em java e NULL em C.
True
False

Funcoes primitivas importantes

print 'ola' imprime ola no stdout. A partir de Python 3.0 o print passou a ser uma função e não uma keyword especial. Assim, a versão correcta em Python 3.o é print('ola')

del x destroi a ligação entre x e o seu valor.

dir(objecto) imprime todos os metodos para este objecto.

help(arg) imprime ajuda sobre arg.

Exemplo:
help(''.center) imprime ajuda sobre metodo center do objecto '' (que é uma string).

Strings
As strings podem ser definidas com ' ou " ou ainda """. usar 'esta forma' ou "esta forma" é absolutamente igual. A vantagem é que não somos obrigados a escapar se quisermos imprimir as aspas. Exemplo:
>>> 'Como quero imprimir uma ", uso a plica sem ter de escapar a aspa'
'Como quero imprimir uma ", uso a plica sem ter de escapar a aspa'
>>>
As 3 aspas seguidas """ são uma forma de criar uma string cujos caracteres brancos são inseridos integralmente na string. Exemplo:
>>> """String com uma
... quebra de linha"""
'String com uma \nquebra de linha'
>>>

Sunday, May 10, 2009

Algumas notas sobre a filosofia de desenho do Python:

Em python tudo é um objecto de 1ª classe. Isto significa que não existem qualquer tipo de restrições enter as operações que se podem fazer a cada tipo de objecto. As funções são objectos de 1ª classe e, como tal, podem ser manipuladas como qualquer outro tipo de objectos. Por exemplo, podem ser passadas para dentro de outras funções, ou serem usadas como valor de retorno de uma função.

Regra geral, não existem construções que só são visíveis em compile-time. Tudo está reificado no runtime..

Directa ou indirectamente tudo herda de object. inclusive números, classes, funções, etc. Não existem tipos especiais (como, por exemplo, os ints em Java). Tudo são objectos.



Por outro lado, a linguagem segue uma filosofia de que o programador deve escrever o mínimo possível. Assim, e como consequência dessa filosofia, existem muito poucas keywords especiais. As variáveis não têm que ser declaradas antes de usadas e não são tipificadas.
Pelo facto de se tentar ter um mínimo de keywords/tokens especiais, não existem keywords para marcar o início de fim de um bloco. É pela identação que o Python sabe quando começa um novo bloco e quando acaba. Exemplo:
if x <> 10 and x < 20):   
print "So vou aparecer se a condicar avaliar para True"

print "Eu ja nao pertenco ao if. logo apareco sempre"

Por outro lado, como as variáveis não são tipificadas nem declaradas, posso
fazer o seguinte:
x = 54
x = "ola"

A atribuição em python é feita com = e a comparação com ==
A ligação entre nomes (de variáveis ou parâmetros de funções) e o seu valor é
sempre feita por referência. i.e. x=3 significa que o x refere-se ao objecto representado por 3.


A linguagem é fortemente tipificada. Não é em geral possível transformar um objecto
de um dado tipo noutro objecto. Como por exemplo em C em que isto é possível
com casts.

A implementação normal de python corre em cima de uma máquina virtual. A compilação é
sempre feita just in time (normalmente quando um módulo é importado).
Python tem também um garbage collector automático.

Wednesday, May 6, 2009

Livro

Gostei tanto da apresentação do Sr. Alex Martelli que acabei de comprar o seu livro sobre Python:
Python in a nutshell - 2nd edition.


O livro tem cerca de 700 páginas sendo as primeiras cerca de 200 dedicadas à linguagem em si e aos módulos standard mais importantes. Os capítulos seguintes referem-se aos diferentes módulos standard para vários tipos de domínios comuns: IO, Persistência e bases de dados, Controlo de execução, threads, processamento numérico, redes, HTML/XML etc.
Para um livro que tem "in a nutshell" no seu título penso que tem um péssimo titulo. Na realidade este livro é mesmo muito completo pois para além de ensinar a linguagem serve também como documentação técnica para os diferentes módulos standard e até alguns que sendo feitos por terceiras entidades são aconselhados.
O livro foi feito numa altura entre a transição da versão 2.4 para a 2.5 do python. Como tal, não apresenta as diferenças posteriores. Relembro que à data da escrita, a versão mais actual do python (e aconselhada) é a 2.6 e que existe também a versão 3.0 com algumas funcionalidades experimentais.

Saturday, May 2, 2009

Outras implementações de Python

Como referi, existem outras implementações desta linguagem. As duas que considero mais interessantes para referir aqui são:
  • Jython
  • IronPython
Jython permite correr código Python em qualquer ambiente Java pois o interpretador/compilador de Python foi escrito em Java. Mas não é só. Código que se executa em Jython pode utilizar quaisquer classes Java disponíveis na VM de java. Infelizmente esta máquina virtual só implementa a versão 2.2 do Python (à data), pelo que certas features da linguagem, introduzidas em versões posteriores, não vão estar disponíveis.

IronPython é um projecto parecido com o anterior mas destinado à Framework .NET da microsoft.

Thursday, April 23, 2009

Evolução

Referi no último post, que Python tem evoluído de acordo com o feedback da comunidade e queria explicar este ponto porque é interessante.
Ao contrário de outras linguagens que não ouvem o que a comunidade diz, o desenvolvimento do Python é fortemente influenciado pela comunidade num processo em que qualquer pessoa pode submeter propostas de alteração da linguagem (chamados "Python Enhancement Proposals") que depois são votadas e eventualmente aceites para implementação.
Neste site http://www.python.org/dev/peps/ podem ver a lista de PEP's e o seu estado.

Tuesday, April 14, 2009

Instalação, implementação de referência

Quando falamos de uma linguagem é necessário distingui-la da sua implementação. Existem várias implementações da linguagem Python (interpretadores/compiladores) de Python. A implementação de referência é aquela que está disponível em python.org e foi escrita em C. Excepto quando explicitamente referido, todo os exemplos presentes neste blog devem ser executados pela referida implementação. Este ponto é importante porque apesar de existirem outras implementações interessantes, estas podem não ter o mesmo nível de compatibilidade que a implementação de referência, chamada CPython.

A instalação de CPython é trivial tanto em windows como em Linux. Em windows o procedimento a executar consiste em ir ao site http://www.python.org/ e fazer o download do instalador para este S.O.. Em linux, é comum encontrar-se o pacote de python nos sistemas de gestão de pacotes. Se não existir, podem encontrar o código fonte no referido site e instruções para o compilar.

Ainda no site referido podem ver que existem (à data) duas versões diferentes disponíveis: 2.6.1 e 3.0.1 . Estas diferenças devem-se ao facto de o Python ser uma linguagem que tem vindo a evoluir de acordo com o feedback da comunidade. Algumas features foram introduzidas e outras retiradas na versão 3.x. Sendo que aconselho a aprendizagem da versão 2.6 e depois consultar as diferenças para a versão 3. Além disso, existem ainda muitas bibliotecas que não estão preparadas para serem executadas com a semântica da versão 3 e, como tal, não vão funcionar.

Muito honestamente não sei ainda quais são as diferenças em concreto, só espero que sejam pequenos pormenores em vez de ter de aprender 2 linguagens completamente diferentes (o que tenho lido é que são, de facto, pequenas diferenças).

Wednesday, April 8, 2009

Mas

Tinha de escolher uma linguagem de muito alto nível. Lisp estava fora de questão:falta-lhe standardização e bibliotecas decentes para problemas comuns.
A linguagem de eleição tinha de ter as seguintes características:
  • Existência de implementações para Windows e Linux.
  • Suporte a programação orientada a objectos.
  • Gestão automática de memória.
  • Expressiva
  • Boas APIS/bibliotecas standard.
  • Grande comunidade de utilizadores e recursos facilmente acessíveis.
Python, Ruby, e Perl pareciam promissoras. Decidi-me a aprender Python em detrimento das outras duas após ver a apresentação que entitulada "Painless Python for Proficient Programmers" de Alex Martelli (em baixo).


Obviamente aconselho vivamente estas apresentações para quem já sabe programar e quer ter uma ideia do que é Python pois ele explica a filosofia por detrás da linguagem e contrasta algumas das suas características com Java e C++.

Thursday, April 2, 2009

Motivação

Precisava de uma linguagem produtiva. Por produtiva entenda-se uma linguagem expressiva e com boas bibliotecas standard. A expressividade é importante porque sou preguiçoso. Prefiro escrever pouco (código) e fazer muito do que o contrário. A existencia de bibliotecas standard é essencial para não ter de perder o meu tempo a programar estruturas de dados básicas. Esta necessidade tournou-se óbvia perante mim quando, num determinado projecto pessoal, cheguei à conclusão que teria de escrever uma quantidade de código que não era compatível com o tempo que tinha (tamos a falar de um projecto pessoal). Mesmo em Java, teria que escrever uma quantidade de código absurda para a simplicidade da tarefa. Decidi que estava na altura de adicionar uma boa linguagem de muito alto nível à colecção de linguagens em que me sinto verdadeiramente à vontade (c,c++,java).