Entendendo “tudo é objeto em Python”

Nessa última semana, li um excelente post sobre o modelo de execução do Python escrito pelo Jeff Knupp e tive vontade de escrever sobre um assunto que é tratado por ele no post: muita gente fala que em Python tudo é um objeto, mas nem todos entendem o que isso realmente significa. Vou tentar ilustrar aqui com um exemplo que acho bem interessante, que é a declaração de classes.

A palavra reservada class

Antes de ler o livro Learning Python, eu tinha uma ideia um pouco distorcida sobre classes em Python. Para mim, a palavra class tinha um quê de mágica envolvida. Com a leitura, aprendi que class é um construtor de objetos, e não somente uma declaração existente no código. Isso porque class é executada pelo interpretador quando encontrada no código, e o resultado dessa execução é um novo objeto existente no escopo do módulo. A diferença deste para um construtor qualquer, é que este constrói objetos do tipo type, permitindo assim que criemos objetos desse tipo recém criado. Vamos ver um exemplo rápido no interpretador:

>>> class Teste(object): pass
>>> print type(Teste)
<type 'type'>

Talvez você esteja pensando: “grande coisa, é a mesma coisa que em outras linguagens orientadas a objetos que já usei…”. Mas se acalme aí, pois o melhor ainda está por vir. Tendo criado o objeto Teste com a declaração class, agora podemos acessá-lo como qualquer outro objeto. Podemos acrescentar atributos à classe:

>>> Teste.x = 0
>>> print Teste.x
0
>>> t1 = Teste()
>>> print t1.x
0

Perceba que acrescentamos o atributo x ao objeto Teste, e que isso faz com que instâncias dessa classe passem a possuir esse atributo. E aí, você fazia isso em Java? Isso permite que, por exemplo, adicionemos métodos a uma classe já existente:

>>> def funcao(self): print 'valor de x:', self.x
>>> Teste.f = funcao
>>> t2 = Teste()
>>> t2.f()
valor de x: 0

Como a palavra def também cria objetos (do tipo function), podemos manipular funções como se fossem outros objetos quaisquer. Preste atenção à linha 2 do trecho acima (Teste.f = funcao) e perceba que não estamos fazendo uma chamada à função funcao. O que fizemos foi criar um atributo f em Teste, e então fizemos com que esse atributo referencie o objeto funcao, que incidentalmente é um objeto “executável”, um objeto function. Assim, todas as instâncias de Teste passam a ter também uma função f em seu escopo.

Feito isso, peço que releia os trechos de código acima e depois, diga se a seguinte linha de código irá funcionar ou não:

>>> t1.f()

O que você acha? A resposta é: a linha acima é executada com sucesso e a saída na tela de sua execução é mostrada abaixo:


valor de x: 0

Algo estranho? Perceba que t1 foi instanciado antes de adicionarmos o método f à classe Teste. Como ele incorporou esse método? Para entender isso, é preciso entender o modelo de resolução de nomes e hierarquia de objetos em Python.

Hierarquia de classes

Quando criamos uma classe, como fizemos com o objeto Teste, estamos criando um objeto que descende de object, que é um objeto do tipo type e que possui vários atributos e métodos que são herdados pelas classes descendentes. Ao criarmos uma instância de Teste, como t1 e t2, estamos criando objetos que descendem de Teste, e por transitividade, descendem também de object. Veja a imagem abaixo:

mro

Essa é a hierarquia existente em tempo de execução em Python. Sempre que tentamos acessar um atributo de um objeto, o interpretador faz o seguinte: verifica se o objeto em questão possui tal atributo; caso não possua, verifica se o objeto que está imediatamente acima na hierarquia possui tal atributo, e faz isso sucessivamente até encontrar ou chegar a object. Se chegar em object e nem ele possuir o atributo procurado, ocorre um AtributeError. Isso é feito para cada acesso a atributos, em tempo de execução. Isso explica o porquê de t1.f() ter funcionado corretamente, mesmo t1 tendo sido criado antes de termos adicionado f a Teste. O que ocorre é bem simples: ao tentar acessar t1.f(), o interpretador busca por f em t1 e não encontra. Assim, busca por f no objeto imediatamente acima de t1, que é Teste, o encontra lá e executa o código do objeto.

Interessante, não? 🙂

Leia mais:

Anúncios

Um comentário sobre “Entendendo “tudo é objeto em Python”

  1. Pingback: Atributos em objetos do tipo function | Python Help

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s