domingo, 15 de novembro de 2009

As funções são objectos

Já ouvimos várias vezes dizer que, em Python, tudo são objectos. Por exemplo, os números (inteiros ou não), as cadeias de caracteres, as listas, os dicionários, os ficheiros, tudo isso são objectos. Dizem-nos, vezes sem conta, que esses objectos têm três atributos fundamentais: identidade, valor e tipo. Quando queremos usar esses objectos podemos usar referência explícitas, como quando calculamos:


>>> 3 + 4
7
>>>

Mas o mais usual é darmos um nome ao objecto e passarmos a referir-mo-nos a esse objecto pelo seu nome:

>>> a = 5
>>> 5 + a
10
>>>

Sempre que usamos a instrução de atribuição criamos uma nova associação entre um nome e um objecto. É claro que um objecto pode ter vários nomes:


>>> a = 5
>>> b = a
>>> id(a)
16793944
>>> id(b)
16793944
>>> b
5
>>>

Mas o que se passa, quando definimos uma função, como em:

>>> def toto(x):
... return 2 * x
...
>>>

Criamos na mesma um objecto do tipo função:

>>> type(toto) # tipo
<type 'function'>
>>> id(toto) # identidade
13426544
>>> toto # valor
<function toto at 0xccdf70>
>>>

Neste caso a descrição do valor é mais complexa (), mas não deixa de ser um objecto. Então pode também ter vários nomes como acontece com os outros tipos de objectos? Claro!

>>> toto(5)
10
>>> tete = toto
>>> tete(5)
10
>>>

Bom, mas então põe-se outra questão. Quando eu defino algo, nessa definição posso ter parâmetros formais, ou argumentos. Na altura de usar o programa eu comunico quais os parâmetros reais e estabeleço uma relação, temporária, entre eles:

>>> a = 5
>>> toto(a)
10
>>>

Os parâmetros formais são sempre nomes de objectos. Os parâmetros reais podem ser nomes, objectos ou expressões cujo valor é uma referência para um objecto.
No exemplo dado, o parâmetro formal x da definição é associado ao parâmetro real a, aquando do uso da definição. Em termos práticos é como se, no início da execução do programa, se tivesse feito x = a, sendo que essa associação é válida durante a execução do programa, caso não seja alterada por outra associação. Mas, e regressamos à questão, será possível associar um parâmetro formal com uma definição? É possível:

>>> def toto(x):
... print x
...
>>> def main(f,x):
... f(x)
...
>>> main(toto,5)
5
>>>

O que é que aconteceu? Bem, durante o uso do programa main, o parâmetro formal f fica associado ao parâmetro real toto. Então, quando tenho a instrução f(x), durante a execução é como se tivesse toto(5). Esta ao ser executada, cumpre o seu papel e imprime 5. Mas esta possibilidade tem algum interesse? Claro que tem. Durante as aulas demos vários exemplos de código, por exemplo, código envolvendo imagens, em que é tudo idêntico menos uma pequena parte. Podemos transformar essa parte, por abstracção, numa nova definição, tornando assim o programa mais geral, mais legível e, pelo menos partes dele, reutilizável:

def manipula_imagem(imagem, funcao_cor):
""" Manipula uma imagem de acordo com uma função."""
largura = imagem.getWidth()
altura = imagem.getHeight()
nova_imagem = cImage.EmptyImage(largura,altura)

for coluna in range(largura):
for linha in range(altura):
pixel = imagem.getPixel(coluna,linha)
novo_pixel = funcao_cor(pixel)
nova_imagem.setPixel(coluna,linha, novo_pixel)
return nova_imagem

Este programa, apresentado nas aulas, pega numa imagem e transforma cada um dos seus pixeis de acordo com uma dada função. Diferentes funcao_cor, originam diferentes resultados:

def pixel_sepia(pixel):
""" Tempo do passado."""
r = pixel.getRed()
g = pixel.getGreen()
b = pixel.getBlue()

novo_r = (r * 0.393 + g * 0.769 + b * 0.189)
novo_g = (r * 0.349 + g * 0.686 + b * 0.168)
novo_b = (r * 0.272 + g * 0.534 + b * 0.131)
if novo_r > 255: novo_r = r
if novo_g > 255: novo_g = g
if novo_b > 255: novo_b = b

novo_pixel = cImage.Pixel(novo_r,novo_g,novo_b)
return novo_pixel

def pixel_cinzento(pixel):
""" Converte um pixel para escala de cinzentos."""
vermelho = pixel.getRed()
verde = pixel.getGreen()
azul = pixel.getBlue()

int_media = (vermelho + verde + azul) / 3
novo_pixel = cImage.Pixel(int_media,int_media, int_media)
return novo_pixel

pixel_sepia e pixel_cinzento são dois exemplos possíveis de funções de cor distintas.

2 comentários:

  1. No segundo exemplo creio qe existe um erro..

    a=5
    5+a = 8

    Nao seria 10??

    Senao poderia explicar?

    ResponderEliminar
  2. Pois. Realmente é 10. Era só para ver se estavam atentos. Nestas contas simples não confio no computador e faço eu mesmo as operações. Às vezes com resultados intrigantes...

    ResponderEliminar