Usando as propriedades definidas em um nível por exemplo por não-classe
-
06-07-2019 - |
Pergunta
O que eu estou tentando alcançar é algo como isto:
class object:
def __init__(self):
WidthVariable(self)
print self.width
#Imagine I did this 60frames/1second later
print self.width
#output:
>>0
>>25
O que eu quero acontecer (como acima): Quando WidthVariable
- uma classe - é criado que acrescenta a variável width
ao objeto instância . Esta variável funciona como uma propriedade normal, mas neste caso particular, é só de leitura (apenas a variável fget
é definido). Além disso, o fget
chama uma função definida em WidthVariable
que decide o que width
para retorno.
No entanto, eu não tenho idéia de como fazer isso! Eu tentei usando propriedades normais, mas eu descobri que eles só trabalham em classes e não por instância - note por favor que o uso de código que eu deveria ser mais semelhante possível ao acima (ou seja, somente o código dentro do __init__
de WidthVariable
deve definir a variável width
, em nenhum outro lugar). Além disso, self.width
não pode ser função, porque eu não o que chamá-lo assim self.width()
, quero self.width
(porque mantém com o resto do design eu tenho).
Para esclarecer, o código completo seria algo como isto:
class MyObject:
def __init__(self)
WidthVariable(self)
print self.width
class WidthVariable:
def __init__(self, object)
object.width = property(self.get_width)
def get_width(self):
value = #do_stuff
return value #The Value
#output:
>>25 #Whatever the Value was
Solução
Uma vez que, como diz @ Jonathan, descritores (incluindo propriedades) são por classe, não por instância, a única maneira de obter diferentes descritores por instância é ter cada instância individualizar sua própria classe. Isso é muito rasa e fácil, tanto quanto metaprogramming vai; -) ...:
class Individualistic(object_or_whatever_bases):
def __init__(self, whatever_args):
self.__class__ = type('GottaBeMe', (self.__class__, object), {})
# keep rocking...!-)
Eu também estou adicionando object
explicitamente porque é necessário (em Python 2.*
, e você dizer que é o que você está usando !!!) para fazer aulas de novo tipo. Nunca use classes de legados mais, eles não se comportam corretamente com relação às propriedades e muito mais, além de (e para compatibilidade com versões anteriores que não pode - em Python 3, as classes legadas foram finalmente aniquilado assim classe CADA é novo estilo sem a exigência de explicitamente herdar de objeto mais!).
Agora, qualquer descritor que é colocado em self.__class__.__dict__
apenas afetará este exemplo, nenhum outro. Há um pouco de sobrecarga (cada classe GottaBeMe
e, portanto, cada instância tem seu próprio __dict__
, etc), mas nada muito terrível.
Agora, tudo que é necessário para satisfazer o pedido original é mudar:
class WidthVariable:
def __init__(self, object)
object.width = property(self.get_width)
(também renomear o arg object
sensivelmente ao pisoteio evitar em um built-in, e fazendo a classe de estilo novo, porque você deve sempre fazer todas as classes de estilo novo ;-), para:
class WidthVariable(object):
def __init__(self, obj)
obj.__class__.width = property(self.get_width)
Nada muito black-magicky, como você pode ver! -)
Outras dicas
Eu não entendo a maneira que você construiu o seu exemplo, nem eu entendo o que você quer dizer com "propriedades normais" trabalhando apenas "em classes". Veja como você criar uma propriedade só de leitura:
class Foo(object):
# create read-only property "rop"
rop = property(lambda self: self._x)
def __init__(self):
self._x = 0
def tick(self):
self._x += 1
f = Foo()
print f.rop # prints 0
f.tick()
f.tick()
print f.rop # prints 2
f.rop = 4 # this will raise AtributeError
Eu acredito que agora eu entendo a sua pergunta, e eu também acredito que você está fora de sorte .
Para as classes de estilo novo, implícita invocações de métodos especiais são única garantido para trabalho corretamente se definida no tipo de um objeto, não em dicionário instância do objeto.
Descritores (que são utilizados para implementar propriedades) deve aparecer no da classe __dict__
, e não pode aparecer no da instância __dict__
. Em outras palavras, Python não é o Ruby!
I correção feliz esperamos de um metaprogrammer Python piedosa, mas eu acho que estou certo.
Não é muito claro o que você quer? mas eu suponho que você quer obj.width para ser personalizado para cada instância Aqui está uma maneira fácil usando propriedades simples, largura valor propriedade retorna retornado por uma por instância de retorno de chamada
class MyClass(object):
def __init__(self, callback):
self.callback = callback
def get_width(self):
return self.callback()
width = property(get_width)
def w1(): return 0
def w2(): return 25
o1 = MyClass(w1)
o2 = MyClass(w2)
print o1.width
print o2.width
Se callback não podem ser passados ??podemos atribuí-la a WidthVariable que retorna largura com base na instância
class MyClass(object):
def __init__(self):
self.callback = WidthVariable(self)
def get_width(self):
return self.callback()
width = property(get_width)
class WidthVariable(object):
def __init__(self, obj):
self.obj = obj
def __call__(self):
return hash(self.obj)
o1 = MyClass()
o2 = MyClass()
print o1.width
print o2.width