Python:在运行时改变方法和属性

我希望创build一个Python类,我可以添加和删除属性和方法。 我怎样才能做到这一点?

哦,请不要问为什么。

我希望创build一个Python类,我可以添加和删除属性和方法。

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' 

这个例子展示了向一个类和一个实例添加一个方法的区别。

 >>> 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' 

在使用types.MethodType一个可能的有趣替代:

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

将是利用function是描述符的事实:

 >>> puppy.talk = talk.__get__(puppy, Dog) 

我希望创build一个Python类,我可以添加和删除属性和方法。 我怎样才能做到这一点?

你可以添加和删除任何类的属性和方法,它们将可用于该类的所有实例:

 >>> 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' 

你可以直接分配给类(通过访问原始类名或通过__class__ ):

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

将打印

 (3, <__main__.a instance at 0x7f18e3c345f0>, 5) (3, <__main__.a instance at 0x7f18e3c344d0>, 7) 

另一种替代方法是,如果需要更换批发类是修改属性:

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

只是:

 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 

这导致:

 ['__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 

这个类本身是否需要修改? 或者仅仅是为了replaceobject.method()在运行时的某个特定位置所做的目标?

我问,因为我避免了在我的框架中使用getattribute和Runtime装饰器在我的Baseinheritance对象上实际修改类到monkey patch特定的方法调用的问题。

getattribute中的Base对象检索的方法被包装在一个Runtime_Decorator中,该Runtime_Decorator分析要应用的修饰器/猴子修补程序的方法调用关键字参数。

这使您能够利用语法object.method(monkey_patch =“mypatch”),object.method(decorator =“mydecorator”),甚至是object.method(decorators = my_decorator_list)。

这适用于任何单独的方法调用(我遗漏了魔法方法),这样做不需要实际修改任何类/实例属性,可以使用任意的,甚至是外部的方法来打补丁,并且将透明地工作在从Baseinheritance的子版本上当然不会覆盖getattribute )。

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

这种方法的缺点是getattribute挂钩对属性的所有访问,所以即使对于不是方法的属性,也不会对特定的调用使用该特性,所以方法的检查和潜在的包装也会发生。 而使用getattribute本质上有些复杂。

在我的经验/为我的目的这种开销的实际影响是微不足道的,我的机器运行一个双核赛扬。 之前的实现我在对象init上使用了introspected方法,然后将Runtime_Decorator绑定到方法。 这样做的事情消除了利用getattribute的需要,并减less了以前提到的开销…但是,它也打破了腌菜(也许不是莳萝),并不那么dynamic,那么这种做法。

用这种技术,我实际遇到的唯一的用例是定时和跟踪装饰器。 然而,它所打开的可能性非常广泛。

如果你有一个先前存在的类不能从一个不同的基础inheritance(或者利用这个技术它是自己的类定义或者它的基类),那么这个事情根本就不适用于你的问题。

我不认为在运行时在类上设置/删除不可调用的属性是非常有挑战性的。 除非你想要从被修改的类inheritance的类自动地自动反映这些变化……这将是一个完整的“不可能的”蠕虫的声音虽然。