python有一个编译好的模块,需要增加一个方法。由于不想修改源代码再编译,所以使用动态绑定方法来给实例增加方法。
第一印象,想到使用如下方法:
def foo(self): print self.name a = A() a.foo = foo
>>> a.foo() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: foo() takes exactly 1 argument (0 given)
结果是无法访问实例的变量。 比较新绑定的方法与原有的实例方法,发现原有的实例方法是bound method。只有bound method才能访问实例的变量。
要动态为实例绑定方法,可以使用new模块(http://docs.python.org/library/new.html)。 (文档中说new模块已经过期,推荐使用types模块。但我看types的文档,想不明白如何取代new模块)
import new a.foo = new.instancemethod(foo, a, A)
问题又来了,新加的方法里有调用实例的私有函数(以双下划线开头),报了如下错误:
class A(): def __private(self): print "private" def public(self): self.__private() def foo(self): self.__private() a = A() import new a.foo = new.instancemethod(foo, a, A) a.foo()
Traceback (most recent call last): File "E:tmptest.py", line 14, in <module> a.foo() File "E:tmptest.py", line 9, in foo self.__private() AttributeError: A instance has no attribute '__private'
通过观察原有方法和动态绑定方法的字节码,发现LOAD_ATTR有差别。原有方法的LOAD_ATTR是“_A__private”,动态绑定的方法的LOAD_ATTR是“__private”
class A(): def __private(self): print "private" def public(self): self.__private() def foo(self): self.__private() a = A() import dis dis.dis(a.public) a.public() import new a.foo = new.instancemethod(foo, a, A) dis.dis(a.foo) a.foo()
6 0 LOAD_FAST 0 (self) 3 LOAD_ATTR 0 (_A__private) # a.public 6 CALL_FUNCTION 0 9 POP_TOP 10 LOAD_CONST 0 (None) 13 RETURN_VALUE private 9 0 LOAD_FAST 0 (self) 3 LOAD_ATTR 0 (__private) # a.foo 6 CALL_FUNCTION 0 9 POP_TOP 10 LOAD_CONST 0 (None) 13 RETURN_VALUE
这里的原因是python会对private方法进行名字粉碎(name mangling) 。因此修改foo方法里面的调用为self._A__private(),通过。但这样修改后,方法对于不同的class会不通用,有待研究更好的方法