Pregunta

Lo que estoy tratando de lograr es algo como esto:

class object:
    def __init__(self):
        WidthVariable(self)

        print self.width
        #Imagine I did this 60frames/1second later
        print self.width

#output:
>>0
>>25

Lo que quiero que suceda (como arriba): cuando se crea WidthVariable , una clase, agrega la variable width al objeto instancia . Esta variable actúa como una propiedad normal, pero en este caso particular es de solo lectura (solo se establece la variable fget ). Además, el fget llama a una función definida en WidthVariable que decide qué width devolver.

Sin embargo, ¡no tengo idea de cómo hacer esto! Lo intenté usando propiedades normales pero descubrí que solo funcionan en clases y no por instancia; tenga en cuenta que el código que uso debe ser lo más similar posible al anterior (es decir, solo el código dentro del __init__ de WidthVariable debería establecer la variable width , en ningún otro lugar). Además, self.width no puede funcionar, porque no sé cómo llamarlo como self.width () , quiero self.width (porque se mantiene con el resto del diseño que tengo).

Para aclarar, el código completo sería algo como esto:

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
¿Fue útil?

Solución

Dado que, como dice @Jonathan, los descriptores (incluidas las propiedades) son por clase, no por instancia, la única forma de obtener diferentes descriptores por instancia es hacer que cada instancia individualice su propia clase. Eso es bastante superficial y fácil en lo que respecta a la metaprogramación; -) ...:

class Individualistic(object_or_whatever_bases):
  def __init__(self, whatever_args):
    self.__class__ = type('GottaBeMe', (self.__class__, object), {})
    # keep rocking...!-)

También estoy agregando object explícitamente porque es necesario (en Python 2. * , ¡y dices que eso es lo que estás usando!) para hacer clases de nuevo tipo. Nunca más use clases heredadas, no se comportan correctamente con respecto a las propiedades y mucho más (y por compatibilidad con versiones anteriores que no pueden: en Python 3, las clases heredadas finalmente se han aniquilado, por lo que CADA clase es de nuevo estilo sin el requisito de heredar explícitamente de un objeto más!).

Ahora, cualquier descriptor que se coloque en self .__ class __.__ dict__ solo afectará esta instancia, ninguna otra. Hay un poco de sobrecarga (cada clase GottaBeMe y, por lo tanto, cada instancia tiene su propio __dict__ , etc.), pero nada demasiado terrible.

Ahora, todo lo que se necesita para satisfacer la solicitud original es cambiar:

class WidthVariable:
    def __init__(self, object)
        object.width = property(self.get_width)

(también renombra el argumento object razonablemente para evitar pisotear un incorporado, y hacer que la clase sea de nuevo estilo porque SIEMPRE debe hacer que CADA clase sea de nuevo estilo ;-), para:

class WidthVariable(object):
    def __init__(self, obj)
        obj.__class__.width = property(self.get_width)

¡Nada demasiado negro-mágico, como puedes ver! -)

Otros consejos

No entiendo la forma en que ha construido su ejemplo, ni entiendo lo que quiere decir sobre "propiedades normales". solo trabajando "en clases". Así es como se crea una propiedad de solo lectura:

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

Creo que ahora entiendo tu pregunta, y también creo que eres sin suerte .

  

Para clases de estilo nuevo, implícito   las invocaciones de métodos especiales son   solo se garantiza que funcione correctamente si   definido en el tipo de un objeto, no en   el diccionario de instancias del objeto.

Los

descriptores (que se utilizan para implementar propiedades) deben aparecer en la clase __dict__ , y no pueden aparecer en la instancia __dict__ . En otras palabras, ¡Python no es Ruby!

Espero felizmente la corrección de un metaprogramador de Python piadoso, pero creo que tengo razón.

¿No tienes muy claro lo que quieres? pero supongo que desea que obj.width se personalice para cada instancia Aquí hay una manera fácil mediante el uso de propiedades simples, la propiedad ancho devuelve el valor devuelto por una devolución de llamada por instancia

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

Si no se puede pasar la devolución de llamada, podemos asignarla a WidthVariable que devuelve el ancho en función de la instancia

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 bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top