domingo, 25 de outubro de 2009

Tipos... com Classe!

Já nos aconteceu a todos ter que preencher formulários, electronicamente ou não. Por exemplo, a declaração de IRS. Esses formulários têm vários campos que devem ser preenchidos. Uns são obrigatórios (nome, número de contribuinte), outros não (deduções fiscais). Pode acontecer que em parte o formulário já venha preenchido, por exemplo, com base em informação retirada do nosso computador. Depois de ser por nós preenchido, os formulários são tratados por um diligente funcionário, podendo eventualmente ser corrigidos, ou seja alterados. Temos assim que um mesmo padrão (o formulário) dá origem a várias instanciações (os formulários parcialmente preenchidos), que por sua vez são completadas por cada um de nós de forma diversa. Finalmente podem, posteriormente, ser objecto de modificações. Mas o que é que tudo isto tem a ver com computadores e com programação? Bem, de um modo directo, muito pouco. Mas como metáfora a ideia de formulário,o seu preenchimento e manipulação pode ser interessante.


Já sabemos que os objectos em programação têm atributos. Por exemplo, identidade, valor e tipo. Assim, 5 e 7 são dois objectos numéricos, de valor 5 e 7, respectivamente, com uma identidade própria (a zona da memória onde estão guardados), e um tipo (inteiro neste caso). Em relação aos tipos também já conhecemos vários: inteiros, inteiros longos, vírgula flutuante, complexos, booleanos, cadeias de caracteres, listas, tuplos e dicionários. A consideração do tipo de um objecto condiciona o valor que ele pode ter ( e o modo como fica armazenado também). O tipo funciona como um formulário vazio. Ao criar um objecto de um determinado tipo estamos a concretizar o modelo, ou formulário, definido pelo tipo. Quando manipulamos o objecto é como se estivéssemos a completar ou modificar o nosso formulário. A forma de o fazer é através do recurso a funções, que por serem específicas dos objectos do tipo, recebem o nome de métodos. Em Python os tipos são implementados recorrendo a classes. Um conceito que por agora iremos deixar por definir. Fique-se apenas com a ideia de que um tipo = classe = modelo. A instanciação da classe dá origem a objectos. Essa instanciação, ou criação de um objecto do tipo, ocorre graças ao recurso a um método especial que, por tal motivo, se designa por construtor. A listagem que apresentamos de seguida ilustra estes factos.


>>> i = int()
>>> i
0
>>> l = long()
>>> l
0L
>>> f = float()
>>> f
0.0
>>> c = complex()
>>> c
0j
>>> b = bool()
>>> b
False
>>> c = str()
>>> c
''
>>> s = str()
>>> s
''
>>> c = complex()
>>> l = list()
>>> l
[]
>>> t = tuple()
>>> t
()
>>> d = dict()
>>> d
{}
>>> type(i)
<type 'int'>
>>> type(b)
<type 'bool'>
>>> type(c)
<type 'complex'>
>>> type(l)
<type 'long'>
>>> type(d)
<type 'dict'>
>>> type(l)
<type 'list'>
>>> type(t)
<type 'tuple'>
>>>


Desde logo uma observação: o nome do construtor é igual ao nome do tipo. Claro que podemos chamar o construtor com um objecto, como no nosso formulário parcialmente preenchido. Eis alguns exemplos.


>>> i = int(5)
>>> i
5
>>> l = list((1,2,3))
>>> l
[1, 2, 3]
>>> d = dict(((1,'a'),(2,'b')))
>>> d
{1: 'a', 2: 'b'}


Não deixam de ser situações curiosas. Estamos mais habituados a fazer simplesmente:

>>> i = 5
>>> i
5
>>> l = [1,2,3]
>>> l
[1, 2, 3]
>>> d = {1:'a',2:'b'}
>>> d
{1: 'a', 2: 'b'}


Mas isto é apenas uma comodidade de notação que a linguagem nos oferece. Como há outras. Veja-se o exemplo:


>>> (5).__add__(3)
8
>>> 5 + 3
8
>>>

Este exemplo mostra que 5 é um objecto, instância da classe (= tipo) int, e que podemos por isso aplicar-lhe o método __add__ que foi definido na classe.


Um outro aspecto é a conversão entre tipos. Essa conversão pode ser feita automaticamente pelo sistema ou, expressamente por nós.


>>> 1/2
0
>>> 1.0/ 2
0.5
>>> 4.3 / (4 + 5j)
(0.41951219512195126-0.52439024390243905j)
>>> i = 3
>>> f = 4.5
>>> c = complex(3,4)
>>> l = [1,2,3]
>>> ccar = 'ernesto costa'
>>> lcar = ['abc','123']
>>> int(f)
4
>>> float(i)
3.0
>>> ccar.split()
['ernesto', 'costa']
>>> '--'.join(lcar)
'abc--123'
>>> complex(i)
(3+0j)
>>> complex(f)
(4.5+0j)
>>> list(ccar)
['e', 'r', 'n', 'e', 's', 't', 'o', ' ', 'c', 'o', 's', 't', 'a']
>>> str(i)
'3'
>>>


As 6 primeiras linhas mostram conversões automáticas pelo sistema. Essas conversões ocorrem quando temos operações com objectos de tipos diferentes. Sempre que possível o de nível mais baixo é convertido no de nível mais elevado. Nas restantes somos nós que forçamos a conversão. Notar como os métodos split e join permitem passar de cadeias de caracteres a listas e vice versa. O leitor interessado pode aprofundar estas questões no manual da linguagem.


Termino referindo uma função (isinstance) que nos permite testar o tipo de um objecto. Isto aplica-se aos objectos pré-definidos bem como a todos os que os novos tipos definidos pelo utilizador como uma classe.


>>> isinstance(i,int)
True
>>> isinstance(c,complex)
True
>>> isinstance(l,list)
True
>>> isinstance(lcar,str)
False
>>>


Veja-se como o nome do tipo não tem plicas!

Sem comentários:

Enviar um comentário