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.

Tuesday, June 30, 2009

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.