2010-06-02 1 views
17

sé que puedo añadir dinámicamente un método de instancia de un objeto haciendo algo como:dinámicamente añadiendo @property en Python

import types 
def my_method(self): 
    # logic of method 
# ... 
# instance is some instance of some class 
instance.my_method = types.MethodType(my_method, instance) 

Más tarde me puede llamar instance.my_method() y auto será enlazado correctamente y todo funciona.

Ahora, mi pregunta: ¿cómo hacer exactamente lo mismo para obtener el comportamiento que la decoración del nuevo método con @property daría?

Conjeturaría algo como:

instance.my_method = types.MethodType(my_method, instance) 
instance.my_method = property(instance.my_method) 

Pero, haciendo que instance.my_method devuelve un objeto de propiedad.

+0

@Bo Persson: Aunque el título de la pregunta _python: ¿Cómo agregar propiedades a una clase dinámicamente? _ Es básicamente el mismo, no creo que su respuesta aceptada (incluido el complemento) aborde la pregunta en general. – martineau

+0

@martineau - Nadie parecía creer eso tampoco, por lo que el voto cercano ha expirado hace mucho tiempo. –

+0

@BoPersson: Estoy corregido, técnicamente la información en la [respuesta agregada] (http://stackoverflow.com/a/1355444/355230) a esa pregunta se dirige a la pregunta general. – martineau

Respuesta

32

Los property objetos descriptor tiene que vivir en la clase , no en el ejemplo, para tener el efecto deseado. Si no desea modificar la clase existente con el fin de no alterar el comportamiento de los otros casos, tendrá que hacer una "clase por instancia", por ejemplo:

def addprop(inst, name, method): 
    cls = type(inst) 
    if not hasattr(cls, '__perinstance'): 
    cls = type(cls.__name__, (cls,), {}) 
    cls.__perinstance = True 
    inst.__class__ = cls 
    setattr(cls, name, property(method)) 

Estoy marcado de estos clases especiales "por instancia" con un atributo para evitar hacer innecesariamente múltiples si está haciendo varias llamadas addprop en la misma instancia.

Tenga en cuenta que, al igual que para otros usos de property, que necesita la clase de juego que sea nuevo estilo (típicamente obtenido por heredar directa o indirectamente de object), no el estilo legado antiguo (cayó en Python 3) que está asignado por defecto a una clase sin bases.

+12

Acabo de seguir un enlace a esta respuesta y me pareció muy útil. Sin embargo, tal vez debido a los cambios en Python en los últimos años, algunas cosas no funcionaron de la caja para mí. Tuve que cambiar 'cls.hasattr ('__ perinstance')' a 'hasattr (cls, '__perinstance')', y tuve que agregar una línea dentro del bloque 'if':' inst .__ class__ = cls'. ¡Espero que esto ayude a cualquier persona que vea esta respuesta para que funcione para ellos! – Blckknght

+8

@Blckknght: No veo cómo la respuesta original podría haber funcionado porque nunca cambia la clase de la instancia a la recién creada "por instancia" cuando eso ocurre. – martineau

3

Dado que esta cuestión no está preguntando por única adición a una instancia spesific, el siguiente método se puede utilizar para agregar una propiedad a la clase, esto expondrá las propiedades a todas las instancias de la clase tu caso es distinto.

cls = type(my_instance) 
cls.my_prop = property(lambda self: "hello world") 
print(my_instance.my_prop) 
# >>> hello world 

Nota: La adición de otro respuesta porque creo @Alex Martelli, mientras correcta, es lograr el resultado deseado mediante la creación de una nueva clase que posee la propiedad, esta respuesta se pretende que sea más directa/sencillo y sin abstracción de lo que sucede en su propio método.

+0

-1, esto no solo afecta el comportamiento de 'mi_instancia', también establece' my_prop' para todas las demás instancias de clase 'tipo (mi_instancia)' –

+0

@ali_m, Correcto, ese es el propósito, es agregar el atributo al clase, la respuesta anterior (que se marca como la correcta). también está agregando la propiedad a la clase (no a la instancia). – ideasman42

+1

No, lo que sucede es que 'cls = type (cls .__ name__, (cls,), {})' crea una nueva clase 'por instancia' cuya base es el tipo de la instancia a la que estamos agregando el atributo . Cuando se asigna 'inst .__ class__ = cls' como en el comentario de Blckknght, esto da como resultado que el atributo solo se aplique a' inst', no a todas las instancias de clase 'type (inst)'. –