Olá Ramon,
> Tem um texto interessante (inclusive já foi mencionado na lista) falando de get e set. Pelo texto [1] que fala de property (propriedades) eu não consegui ver as vantagens.
Em outras linguagens (como Java), utilizamos métodos get* e set*
(exemplo, setNome e getNome) para encapsular a origem e/ou o
tratamento dos dados.
Exemplo: se, num cadastro de pessoas, quiséssemos gravar todos os
nomes em maiúsculas (independente da entrada que o usuário forneceu
ser ou não maiúscula) usaríamos algo do gênero (não lembro da sintaxe
exata):
public void setNome(String nome) {
this.nome = nome.toUpper();
}
Então colocaríamos o atributo "nome" como private e só permitiríamos
acesso via getNome() e setNome(). Veja esta implementação em Python:
>>> class Pessoa(object):
>>> def __init__(self):
>>> self.__nome = ''
>>> def setNome(self, nome):
>>> self.__nome = nome.upper()
>>> def getNome(self):
>>> return self.__nome
>>> nome = property(getNome, setNome)
Podemos fazer:
>>> p = Pessoa()
>>> p.setNome('Dirceu')
>>> print p.getNome() # imprime 'DIRCEU'
Ou, de um jeito mais natural:
>>> p = Pessoa()
>>> p.nome = 'Dirceu' # invoca setNome
>>> print p.nome # invoca getNome e imprime 'DIRCEU'
A vantagem do uso de property é que o código fica mais legível e o
tratamento dos dados fica transparente.
Acho que é isso. Alguém complementa?
--
Dirceu Pereira Tiegs - http://dirceu.info/
Weimar Consultoria
========================================================
Antes de enviar sua mensagem dê uma lida em:
http://www.pythonbrasil.com.br/moin.cgi/AntesDePerguntar
========================================================
Links do Yahoo! Grupos
<*> Para visitar o site do seu grupo na web, acesse:
http://br.groups.yahoo.com/group/python-brasil/
<*> Para sair deste grupo, envie um e-mail para:
python-brasi...@yahoogrupos.com.br
<*> O uso que você faz do Yahoo! Grupos está sujeito aos:
http://br.yahoo.com/info/utos.html
O que eu vejo como vantagem também usando property é a manutenção e
extensão do seu código.
Seguindo o exemplo do Dirceu...
Supomos que esse cadastro em sua primeira versão não tinha a exigência
de passar todos os nomes para maiúsculas, então o código na primeira
versão seria:
>>> class Pessoa(object):
>>> def __init__(self):
>>> self.nome = ''
Utilizando desta forma:
>>> p = Pessoa()
>>> p.nome = 'Dirceu' # atribui o valor diretamente no atributo
>>> print p.nome # acessa diretamente o atributo
Com o passar do tempo perceberiam a necessidade de passar todos os nomes
para maiúsculas.
A alteração seria apenas na classe Pessoa, a *utilização* ficaria
exatamente a mesma.
Essa eu acho que é a principal vantagem de *poder* usar property, um
atributo que não tinha nenhum tratamento, pode ser alterado para ter um
tratamento/encapsulamento sem alterar a utilização do mesmo.
Essa abordagem permite você escrever códigos simples num primeiro
momento e se houver a necessidade permite extender a funcionalidade de
forma bem natural.
Até mais,
--
Luciano Pacheco
http://lucmult.blogspot.com/
A vantagem é que com properties você consegue tratar coisas complexas como
se fossem atributos públicos, que são simples de acessar e modificar (e a
sintaxe mais limpa).
Em linguagens que não suportam properties você é OBRIGADO a criar
getter/setter para atributos que você não quer que o usuário tenha acesso
total. Assim, nessas linguagens, você tem que chamar o setter para setar
alguma coisa, mas não precisa chamar nada para fazer atribuição em atributo
público (ou que você tem acesso).
Ficou complicado. Vou dar um exemplo:
em Python, depois de ter declarado a property OU usando atributos "públicos"
objeto.atributo = valor (para fazer atribuicao)
objeto.atributo (para fazer leitura)
em linguagens que não suportam existem 2 sintaxes:
se for atributo público (ou que eu tenho acesso):
objeto.atributo = valor (atribuição)
objeto.atributo (leitura)
se for privado, depois de ter declarado os getter/setters:
objeto.setAtributo(valor)
objeto.getAtributo()
ou seja. 2 sintaxes (e a com getters/setters é mais poluída).
em Python, não faz a menor diferença se é "privado" ou não.
por exemplo, no pygame:
<codigo>
>>> import pygame
>>> r = pygame.Rect(0,0,10,10)
>>> r
<rect(0, 0, 10, 10)>
>>> r.width
10
>>> r.center
(5, 5)
>>> r.width = 6
>>> r.center
(3, 5)
</code>
width foi acessado como se fosse um atributo qualquer. é este tipo de
transparência que a property possibilita.
Entendeu?
On 8/4/06, Ramon Vinas < ramonvi...@yahoo.com.br> wrote:
>
> Pessoal,
>
> Tem um texto interessante (inclusive já foi mencionado na lista) falando
> de get e set. Pelo texto [1] que fala de property (propriedades) eu não
> consegui ver as vantagens.
> Alguêm poderia fazer algum comentário.
--
Abraços
Leandro Lameiro
Blog: http://lameiro.redirectme.net/blog
[As partes desta mensagem que não continham texto foram removidas]
>
> Ou seja, se o atributo nome for da classe funciona beleza, se for da
> instância não faz o que eu quero. :(
>
> Você sabe explicar o porque ?
>
> Dei uma lida em [1] e não achei nada a respeito.
>
>
> [1] - http://www.python.org/doc/current/ref/descriptor-invocation.html
Isso vai render... :)
Isso acontece porque eles têm de ser atributos da classe, porque é a
metaclasse que lida com os descriptors quando são criados. Eles são
ligados às instâncias dinamicamente na execução, para poder receber a
própria instância como argumento implícito. Por exemplo, isso que você
fez funciona com o __get__ na classe, sem uma instância:
<code>
>>> class D(object):
... def __get__(self, obj, cls):
... print obj, cls
... return 1
...
>>> class C(object):
... x = D()
...
>>> C.x
None <class '__main__.C'>
1
>>>
</code>
Veja que aí a instância aparece como None. Saber explicar o porquê disso
eu até sei, mas é complicado... explicando primeiro com métodos é mais
fácil... mas tem uma surpresa no final...
Um método é apenas um objeto função, mas para receber implicitamente a
instância como o primeiro parâmetro ele tem de ser ligado a ela. Apenas
ser um atributo não basta, ele continua sendo o mesmo objeto. Quando
chamamos ele não recebe a instância no self e a chamada falha.
<code>
>>> class C(object):
... pass
...
>>> def f(self):
... print self
...
>>> f
<function f at 0xb7d02d4c>
>>> o = C()
>>> o.f = f
>>> o.f
<function f at 0xb7d02d4c>
>>> o.f()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: f() takes exactly 1 argument (0 given)
</code>
Se atribuirmos à classe, em relação a ela se torna um método "unbound",
ligado à classe, e desligado em relação a qualquer instância. Quando
você cria a instância ele é ligado a ela. A chamada na instância agora
funciona:
<code>
>>> del o.f
>>> C.f = f
>>> C.f
<unbound method C.f>
>>> o.f
<bound method C.f of <__main__.C object at 0xb7d0a36c>>
>>> o.f()
<__main__.C object at 0xb7d0a36c>
>>> o.f is not C.f is not f
True
</code>
Mas veja como são três objetos distintos, a função original, o método
"unbound" e o método "bound", ligado a instância.
Só pra não ficar de fora, você pode ligar o método explicitamente direto
à instância. Discutimos isso aqui na lista anteriormente (procure por
singletonmethod no gmane).
<code comment="Não façam isso em casa!">
>>> del C.f
>>> o.f()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: 'C' object has no attribute 'f'
>>> import types
>>> types.MethodType
<type 'instancemethod'>
>>> o.f = types.MethodType(f, o)
>>> o.f()
<__main__.C object at 0xb7d0a36c>
>>> x = C()
>>> x.f()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: 'C' object has no attribute 'f'
</code>
Ou seja, logo antes, o objeto função f não era o mesmo que o unbound
method C.f e nem o mesmo que o bound method o.f... o que acontece é que
esses dois tipos de métodos "bound" e "unbound", são apenas um wrapper
para a função original.
A surpresa que falei lá em cima, é que métodos (em classes new-style)
são justamente um descriptor, usado como wrapper para uma função, assim
como property, classmethod e staticmethod. A diferença é que quando isso
ocorre dentro do namespace da classe, no comando 'class' (ou no
__setattr__), a metaclasse automaticamente "embrulha" a função com o
descriptor quando recebe todos os atributos.
O instancemethod, ou types.MethodType é esse descriptor usado para poder
fazer a chamada à função original criada como método, passando a
instância implicitamente. O método __call__ dele recebe os argumentos e
faz a chamada passando a instância como primeiro argumento e o resto. O
classmethod é a mesma coisa, mas passa a classe ao invés da instância. O
staticmethod também é a mesma coisa, mas não altera em nada, passa tudo
direto.
A implementação dele em código Python (sem controle de erro nem nada)
seria mais ou menos isso (espero que dê pra entender):
<code>
class Method(object):
def __init__(self, func, instance, cls):
self.im_func = func
self.im_self = instance
self.im_class = cls
def __get__(self, obj, cls):
return Method(self.im_func, obj, cls)
def __repr__(self):
if self.im_self is None:
return '<metodo desligado %s.%s>'%(self.im_class.__name__, self.im_func.func_name)
else:
return '<metodo %s ligado a instancia %s>'%(self.im_func.func_name, self.im_self)
def __call__(self, *args, **kwds):
if self.im_self is None:
return self.im_func(*args, **kwds)
else:
return self.im_func(self.im_self, *args, **kwds)
</code>
Ou seja, ele recebe a função, a instância onde será ligado (ou None) e a
classe. Quando o atributo é consultado, ele retorna a função original,
"embrulhada" em outra instância dele. Se for consultado na classe (por
isso o __get__ funciona diretamente com classes, como disse lá em cima),
retorna o método unbound, com im_self = None. Se for consultado na
instância, retorna o método ligado, com a instância em im_self. Quando
ele for chamado, dependendo do estado de im_self, decide o que vai
fazer... na verdade na implementação real, o __call__ exige uma
instância de im_class, caso contrário dá TypeError.
Só pra mostrar o que acontece com o código funcionando:
<code>
>>> def f(self):
... print "f chamado com", self
...
>>> class C(object):
... pass
...
>>> C.f = Method(f, None, C)
>>> C.f
<metodo desligado C.f>
>>> o = C()
>>> o.f
<metodo f ligado a instancia <__main__.C object at 0xb7c838cc>>
>>> o.f()
f chamado com <__main__.C object at 0xb7c838cc>
</code>
Isso é 95% equivalente ao que acontece quando você faz:
<code>
>>> class C(object):
... def f(self):
... print "f chamado com", self
...
>>> C.f
<unbound method C.f>
>>> o = C()
>>> o.f
<bound method C.f of <__main__.C object at 0xb7c838ec>>
>>> o.f()
f chamado com <__main__.C object at 0xb7c838ec>
>>>
</code>
Respondida a sua pergunta ? Na verdade ocorre o inverso do que você
imaginava... descriptors têm de ser ligados à classe para receber a
instância implicitamente, justamente porque são usados para implementar
os métodos. Você até pode fazer como queria, mas então em algum lugar
vai ter de passar a instância explicitamente, como no caso do
singletonmethod que mencionei ali em cima.
Sabendo lidar com eles você altera a criação e as chamadas de métodos
como quiser. O momento que você compreender bem como funcionam
descriptors e metaclasses em Python, você está iluminado, como disse o
Osvaldo depois do 1º dia da PyconBrasil desse ano. Toda a linguagem fica
muito mais clara na sua mente, e é quando você percebe que pode fazer
quase tudo...
O problema é só saber o que você deve fazer ou não. :)
É isso aí... e aprendi uma coisa nova. Um detalhe que eu nunca tinha me
tocado e só pensei agora, ao fazer essa implementação aí em cima:
<code>
>>> class C(object):
... def f(self):
... pass
... x = 1
...
>>> C.x is C.x
True
>>> C.f is C.f
False
>>> o = C()
>>> o.f is o.f
False
>>> a = o.f
>>> b = o.f
>>> a is b
False
>>>
</code>
Estranho ? :) Como a cada vez que você acessa o método ele é criado
através do MethodType.__get__, você recebe uma nova instância. Qualquer
hora acho que vou fazer um teste e ver se realmente é algo
significativo. Talvez para alguém que vá executar o mesmo método muitas
vezes, valha a pena atribui-lo a uma variável local e usá-lo só a partir
dela.
Abraço,
--
Pedro Werneck