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
Foi útil?

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
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top