Deseo crear una clase en Python que pueda agregar y eliminar atributos y métodos. ¿Cómo puedo lograr eso?Python: cambio de métodos y atributos en el tiempo de ejecución
Ah, y por favor no preguntes por qué.
Deseo crear una clase en Python que pueda agregar y eliminar atributos y métodos. ¿Cómo puedo lograr eso?Python: cambio de métodos y atributos en el tiempo de ejecución
Ah, y por favor no preguntes por qué.
deseo de crear una clase en Python que puedo agregar y eliminar atributos y métodos.
import types
class SpecialClass(object):
@classmethod
def removeVariable(cls, name):
return delattr(cls, name)
@classmethod
def addMethod(cls, func):
return setattr(cls, func.__name__, types.MethodType(func, cls))
def hello(self, n):
print n
instance = SpecialClass()
SpecialClass.addMethod(hello)
>>> SpecialClass.hello(5)
5
>>> instance.hello(6)
6
>>> SpecialClass.removeVariable("hello")
>>> instance.hello(7)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'SpecialClass' object has no attribute 'hello'
>>> SpecialClass.hello(8)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: type object 'SpecialClass' has no attribute 'hello'
Tenga en cuenta que esto agrega un método de clase a SpecialClass. * No * agrega un método que estará disponible para todas las instancias futuras de SpecialClass. (Me pregunto si hay una manera de hacerlo * que *.) _ –
_ Eso sería realmente interesante. – Glycan
Este ejemplo muestra las diferencias entre agregar un método a una clase y a una instancia.
>>> class Dog():
... def __init__(self, name):
... self.name = name
...
>>> skip = Dog('Skip')
>>> spot = Dog('Spot')
>>> def talk(self):
... print 'Hi, my name is ' + self.name
...
>>> Dog.talk = talk # add method to class
>>> skip.talk()
Hi, my name is Skip
>>> spot.talk()
Hi, my name is Spot
>>> del Dog.talk # remove method from class
>>> skip.talk() # won't work anymore
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: Dog instance has no attribute 'talk'
>>> import types
>>> f = types.MethodType(talk, skip, Dog)
>>> skip.talk = f # add method to specific instance
>>> skip.talk()
Hi, my name is Skip
>>> spot.talk() # won't work, since we only modified skip
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: Dog instance has no attribute 'talk'
Tenga en cuenta que solo puede hacer esto a * classes *, not * instances *. Si lo haces puppy.talk = talk, talk no será un "método vinculado", es decir, no obtendrá el argumento "self" implícito. –
Para aumentar el comentario de Paul: si desea aplicar un método de instancia de monkeypatch: "tipos de importación; f = types.MethodType (talk, puppy, Dog); puppy.talk = f" –
+1 a Paolo para demostrar el efecto dinámico de la asignación y eliminar los atributos del método de clase. –
Una alternativa posiblemente interesante para el uso types.MethodType
en:
>>> f = types.MethodType(talk, puppy, Dog)
>>> puppy.talk = f # add method to specific instance
sería aprovechar el hecho de que las funciones son descriptors:
>>> puppy.talk = talk.__get__(puppy, Dog)
Acabo de enterarme de algo :) Pero creo que parece menos legible. – NicDumZ
+1 Buena sintaxis alternativa, como dices.Tengo curiosidad: ¿hay algún beneficio particular para este enfoque, o para usar "tipos"? En definitiva, producen el mismo resultado y enlaces internos AFAICAT. Does types.MethodType efectivamente produce un descriptor, o hay más en el trabajo? –
@NicDumZ, sí, las cosas __ nunca se ven realmente bien. @Jarret, hubo en algún momento del diseño de Python 3 charlas sueltas sobre la abolición del módulo 'tipos', pero se mantuvo, adelgazado de 37 entradas a 12 (el 'nuevo' módulo se fue, ¡sí! -). Semánticamente son realmente iguales: MethodType devuelve el mismo tipo de objeto que es el resultado de __get__ - una instancia de
deseo de crear una clase en Python que puedo añadir y eliminar atributos y métodos. ¿Cómo puedo lograr eso?
Puede añadir y eliminar atributos y métodos de cualquier clase, y que estará disponible para todas las instancias de la clase:
>>> def method1(self):
pass
>>> def method1(self):
print "method1"
>>> def method2(self):
print "method2"
>>> class C():
pass
>>> c = C()
>>> c.method()
Traceback (most recent call last):
File "<pyshell#62>", line 1, in <module>
c.method()
AttributeError: C instance has no attribute 'method'
>>> C.method = method1
>>> c.method()
method1
>>> C.method = method2
>>> c.method()
method2
>>> del C.method
>>> c.method()
Traceback (most recent call last):
File "<pyshell#68>", line 1, in <module>
c.method()
AttributeError: C instance has no attribute 'method'
>>> C.attribute = "foo"
>>> c.attribute
'foo'
>>> c.attribute = "bar"
>>> c.attribute
'bar'
otra alternativa, si es necesario sustituir la clase es por mayor para modificar la clase atributo:
>>> class A(object):
... def foo(self):
... print 'A'
...
>>> class B(object):
... def foo(self):
... print 'Bar'
...
>>> a = A()
>>> a.foo()
A
>>> a.__class__ = B
>>> a.foo()
Bar
Interesante, pero el objetivo era modificar methods @ runtime. Esto parece una idea para contenedor IoC con conmutación en tiempo de ejecución :) – Migol
Sí, la clase de conmutación le permite modificar métodos al por mayor, especialmente cuando se agrega esto con el hecho de que python permite herencia múltiple y las clases de python son mutables, puede conducir a poderosas técnicas dinámicas o código muy poco sostenible. –
Simplemente:
f1 = lambda:0 #method for instances
f2 = lambda _:0 #method for class
class C: pass #class
c1,c2 = C(),C() #instances
print dir(c1),dir(c2)
#add to the Instances
c1.func = f1
c1.any = 1.23
print dir(c1),dir(c2)
print c1.func(),c1.any
del c1.func,c1.any
#add to the Class
C.func = f2
C.any = 1.23
print dir(c1),dir(c2)
print c1.func(),c1.any
print c2.func(),c2.any
que se traduce en:
['__doc__', '__module__'] ['__doc__', '__module__']
['__doc__', '__module__', 'any', 'func'] ['__doc__', '__module__']
0 1.23
['__doc__', '__module__', 'any', 'func'] ['__doc__', '__module__', 'any', 'func']
0 1.23
0 1.23
sólo se puede asignar directamente a la clase (o bien accediendo al nombre de la clase original o mediante __class__
):
class a : pass
ob=a()
ob.__class__.blah=lambda self,k: (3, self,k)
ob.blah(5)
ob2=a()
ob2.blah(7)
imprimirá
(3, <__main__.a instance at 0x7f18e3c345f0>, 5)
(3, <__main__.a instance at 0x7f18e3c344d0>, 7)
¿La clase en sí misma necesita ser modificada? ¿O el objetivo es simplemente reemplazar lo que object.method() hace en un punto particular durante el tiempo de ejecución?
Lo pregunto porque evito el problema de modificar realmente las llamadas a métodos específicos de parche de clase a mono en mi marco con getattribute y un Decodificador de tiempo de ejecución en mi objeto Base de herencia.
Métodos recuperados por un objeto Base de getAttribute se envuelven en una Runtime_Decorator que analiza el método llama argumentos clave para decoradores/mono parches a aplicar.
Esto le permite utilizar la sintaxis object.method (monkey_patch = "mypatch"), objeto.método (decorator = "mydecorator") e incluso object.method (decorators = my_decorator_list).
Esto funciona para cualquier llamada de método individual (dejo los métodos de magia), lo hace sin modificar ningún atributo de clase/instancia, puede utilizar métodos arbitrarios, incluso ajenos al parche, y funcionará transparentemente en subcatas que heredan de Base (siempre que no anulen getattribute, por supuesto).
import trace
def monkey_patched(self, *args, **kwargs):
print self, "Tried to call a method, but it was monkey patched instead"
return "and now for something completely different"
class Base(object):
def __init__(self):
super(Base, self).__init__()
def testmethod(self):
print "%s test method" % self
def __getattribute__(self, attribute):
value = super(Base, self).__getattribute__(attribute)
if "__" not in attribute and callable(value):
value = Runtime_Decorator(value)
return value
class Runtime_Decorator(object):
def __init__(self, function):
self.function = function
def __call__(self, *args, **kwargs):
if kwargs.has_key("monkey_patch"):
module_name, patch_name = self._resolve_string(kwargs.pop("monkey_patch"))
module = self._get_module(module_name)
monkey_patch = getattr(module, patch_name)
return monkey_patch(self.function.im_self, *args, **kwargs)
if kwargs.has_key('decorator'):
decorator_type = str(kwargs['decorator'])
module_name, decorator_name = self._resolve_string(decorator_type)
decorator = self._get_decorator(decorator_name, module_name)
wrapped_function = decorator(self.function)
del kwargs['decorator']
return wrapped_function(*args, **kwargs)
elif kwargs.has_key('decorators'):
decorators = []
for item in kwargs['decorators']:
module_name, decorator_name = self._resolve_string(item)
decorator = self._get_decorator(decorator_name, module_name)
decorators.append(decorator)
wrapped_function = self.function
for item in reversed(decorators):
wrapped_function = item(wrapped_function)
del kwargs['decorators']
return wrapped_function(*args, **kwargs)
else:
return self.function(*args, **kwargs)
def _resolve_string(self, string):
try: # attempt to split the string into a module and attribute
module_name, decorator_name = string.split(".")
except ValueError: # there was no ".", it's just a single attribute
module_name = "__main__"
decorator_name = string
finally:
return module_name, decorator_name
def _get_module(self, module_name):
try: # attempt to load the module if it exists already
module = modules[module_name]
except KeyError: # import it if it doesn't
module = __import__(module_name)
finally:
return module
def _get_decorator(self, decorator_name, module_name):
module = self._get_module(module_name)
try: # attempt to procure the decorator class
decorator_wrap = getattr(module, decorator_name)
except AttributeError: # decorator not found in module
print("failed to locate decorators %s for function %s." %\
(kwargs["decorator"], self.function))
else:
return decorator_wrap # instantiate the class with self.function
class Tracer(object):
def __init__(self, function):
self.function = function
def __call__(self, *args, **kwargs):
tracer = trace.Trace(trace=1)
tracer.runfunc(self.function, *args, **kwargs)
b = Base()
b.testmethod(monkey_patch="monkey_patched")
b.testmethod(decorator="Tracer")
#b.testmethod(monkey_patch="external_module.my_patch")
La desventaja de este enfoque es getAttribute ganchos todo acceso a los atributos, por lo que la comprobación de envoltura y el potencial de los métodos ocurre incluso para atributos que no son métodos + no serán Utilizando la función de la llamada particular en cuestión. Y usar getattribute en absoluto es algo complicado.
El impacto real de esta sobrecarga en mi experiencia/para mis propósitos ha sido insignificante, y mi máquina funciona con un Celeron de doble núcleo. La implementación anterior utilicé métodos introspectados en el objeto init y luego vinculé el Runtime_Decorator a los métodos. Hacer las cosas de esa manera eliminó la necesidad de utilizar getattribute y redujo los gastos generales mencionados anteriormente ... sin embargo, también se rompe pickle (tal vez no eneldo) y es menos dinámico que este enfoque.
Los únicos casos de uso que he encontrado en la vida real con esta técnica fueron los decoradores de tiempo y trazado. Sin embargo, las posibilidades que abre son extremadamente amplias.
Si tiene una clase preexistente que no puede heredar desde una base diferente (o utiliza la técnica su propia definición de clase o en su clase base '), entonces todo el asunto simplemente no se aplica a su problema en absoluto Desafortunadamente.
No creo que establecer/eliminar atributos no llamables en una clase en tiempo de ejecución sea necesariamente tan desafiante? a menos que desee que las clases heredadas de la clase modificada también reflejen automáticamente los cambios en sí mismas ... Sin embargo, eso podría ser un conjunto de gusanos.
¿Duplicado? http://stackoverflow.com/questions/972/adding-a-method-to-an-existing-object –
¿Quieres saber cómo hacer pato en python? http://en.wikipedia.org/wiki/Duck_punching – baudtack
upvoted por preguntar para no preguntar por qué – oulenz