Python: changing methods and attributes at runtime

I want to create a class in Python to add and remove attributes and methods. How can i do this?

Oh, and please don't ask why.

+67
python reflection runtime
Jun 07 '09 at 22:39
source share
8 answers

I want to create a class in Python to add and remove attributes and methods.

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' 
+42
Jun 08 '09 at 9:24
source share

This example shows the differences between adding a method to a class and an instance.

 >>> 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' 
+110
Jun 07 '09 at 22:42
source share

Perhaps an interesting alternative to using types.MethodType in:

 >>> f = types.MethodType(talk, puppy, Dog) >>> puppy.talk = f # add method to specific instance 

will use the fact that function descriptors :

 >>> puppy.talk = talk.__get__(puppy, Dog) 
+27
Jun 08 '09 at 1:32
source share

I want to create a class in Python to add and remove attributes and methods. How can i do this?

You can add and remove attributes and methods for any class, and they will be available for all instances of the class:

 >>> 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' 
+5
Jun 08 '09 at 19:16
source share

you can simply assign directly to the class (either by accessing the original class name, or via __class__ ):

 class a : pass ob=a() ob.__class__.blah=lambda self,k: (3, self,k) ob.blah(5) ob2=a() ob2.blah(7) 

will print

 (3, <__main__.a instance at 0x7f18e3c345f0>, 5) (3, <__main__.a instance at 0x7f18e3c344d0>, 7) 
+4
Apr 11 '13 at 18:46 on
source share

Another alternative, if you need to replace the wholesale class, you need to change the class attribute:

 >>> 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 
0
Nov 04 '12 at 19:23
source share

Simply:

 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 

that leads to:

 ['__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 
0
Jan 21 '13 at 10:15
source share

Does the class itself need to be changed? Or is the goal just to replace what object.method () does at a certain point at runtime?

I ask because I circumvented the problem of actually changing the class in calling the patch method for monkey in my structure using getattribute and Runtime Decorator on my Base inheritance object.

The methods received by the base object in getattribute are wrapped in a Runtime_Decorator, which parses a method that invokes method keyword arguments to fix decorators / monkeys.

This allows you to use the syntax object.method (monkey_patch = "mypatch"), object.method (decorator = "mydecorator") and even object.method (decorators = my_decorator_list).

This works for any individual method call (I do not take magic methods into account), does it without actually changing the attributes of the class / instance, can use arbitrary, even foreign methods to fix it, and will work transparently on sublayers that inherit from Base (if they do not override getattribute , of course).

 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 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") 

The disadvantage of this approach is that getattribute intercepts all access to attributes, so checking and potential packaging of methods even for attributes that are not + methods will not use the function for a specific call. And using getattribute in general is a bit trickier.

The actual impact of this overhead in my experience / for my purposes was negligible, and the dual-core Celeron runs on my machine. In a previous implementation, I used introspective methods for an init object and associated Runtime_Decorator with methods. Running this method eliminated the need to use getattribute and reduced the overhead mentioned earlier ... however, it also breaks up a pickle (maybe not dill) and is less dynamic than this approach.

The only use cases that I actually encountered β€œin the wild” using this technique were with the timing and tracing of decorators. However, the opportunities that it opens up are extremely broad.

If you have a pre-existing class that cannot be inherited from another base (or use the method that it owns the class definition or in its base class), then all this simply does not apply to your problem at all, unfortunately.

I don’t think that setting / removing non-invoking attributes for a class at runtime is necessarily a daunting task? if you do not want the classes that inherit from the changed class to automatically reflect the changes in themselves ... This would be a "black worm" worms throughout the sound.

0
Sep 14 '14 at 18:35
source share



All Articles