将方法添加到现有的对象实例

我读过,可以在Python中添加一个方法到现有的对象(例如不在类定义中)。

我明白这样做并不总是好事。 但是,怎么会这样呢?

在Python中,函数和绑定方法是有区别的。

>>> def foo(): ... print "foo" ... >>> class A: ... def bar( self ): ... print "bar" ... >>> a = A() >>> foo <function foo at 0x00A98D70> >>> a.bar <bound method A.bar of <__main__.A instance at 0x00A9BC88>> >>> 

绑定的方法已经被绑定(如何描述)到一个实例,并且该实例将在调用该方法时作为第一个parameter passing。

然而,类的属性(而不是实例)的可调参数仍然是未绑定的,因此您可以随时修改类定义:

 >>> def fooFighters( self ): ... print "fooFighters" ... >>> A.fooFighters = fooFighters >>> a2 = A() >>> a2.fooFighters <bound method A.fooFighters of <__main__.A instance at 0x00A9BEB8>> >>> a2.fooFighters() fooFighters 

先前定义的实例也被更新(只要它们本身没有重写属性):

 >>> a.fooFighters() fooFighters 

问题出现在您想要将方法附加到单个实例时:

 >>> def barFighters( self ): ... print "barFighters" ... >>> a.barFighters = barFighters >>> a.barFighters() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: barFighters() takes exactly 1 argument (0 given) 

该函数直接附加到实例时不会自动绑定:

 >>> a.barFighters <function barFighters at 0x00A98EF0> 

为了绑定它,我们可以在types模块中使用MethodType函数 :

 >>> import types >>> a.barFighters = types.MethodType( barFighters, a ) >>> a.barFighters <bound method ?.barFighters of <__main__.A instance at 0x00A9BC88>> >>> a.barFighters() barFighters 

这次这个class的其他class级没有受到影响:

 >>> a2.barFighters() Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: A instance has no attribute 'barFighters' 

阅读关于描述符和元类 编程的更多信息。

新的模块已被弃用,因为Python 2.6和3.0中删除,使用types

请参阅http://docs.python.org/library/new.html

在下面的例子中,我故意从patch_me()函数中删除了返回值。 我认为给予返回值可能会让人相信补丁返回一个新的对象,这是不正确的 – 它修改了传入的对象。 可能这可以促进更加严谨的使用monkeypatching。

 import types class A(object):#but seems to work for old style objects too pass def patch_me(target): def method(target,x): print "x=",x print "called from", target target.method = types.MethodType(method,target) #add more if needed a = A() print a #out: <__main__.A object at 0x2b73ac88bfd0> patch_me(a) #patch instance a.method(5) #out: x= 5 #out: called from <__main__.A object at 0x2b73ac88bfd0> patch_me(A) A.method(6) #can patch class too #out: x= 6 #out: called from <class '__main__.A'> 

将方法添加到现有的对象实例

我读过,可以在Python中添加一个方法到现有的对象(例如不在类定义中)。

我明白,这并不总是一个好的决定。 但是,怎么会这样呢?

是的,这是可能的。 (但不推荐。)

既然这是有启发性的,我会告诉你一些这样做的方法。

这里有一些设置代码。 我们需要一个类的定义。 它可以被导入,但它并不重要。

 class Foo(object): '''An empty class to demonstrate adding a method to an instance''' 

创build一个实例:

 foo = Foo() 

创build一个方法添加到它:

 def sample_method(self, bar, baz): print(bar + baz) 

方法naught(0) – 使用描述符方法, __get__

对函数的虚线查找使用实例调用函数的__get__方法,将对象绑定到方法,从而创build“绑定方法”。

 foo.sample_method = sample_method.__get__(foo) 

现在:

 >>> foo.sample_method(1,2) 3 

方法一 – types。方法types

首先,导入types,我们将从中获取方法构造函数:

 import types 

现在我们将该方法添加到实例中。 要做到这一点,我们需要types模块(我们上面导入)的MethodType构造函数。

types.MethodType的参数签名是(function, instance, class)

 foo.sample_method = types.MethodType(sample_method, foo, Foo) 

和用法:

 >>> foo.sample_method(1,2) 3 

方法二:词法绑定

首先,我们创build一个将方法绑定到实例的包装函数:

 def bind(instance, method): def binding_scope_fn(*args, **kwargs): return method(instance, *args, **kwargs) return binding_scope_fn 

用法:

 >>> foo.sample_method = bind(foo, sample_method) >>> foo.sample_method(1,2) 3 

方法三:functools.partial

部分函数将第一个参数应用于一个函数(也可以select关键字参数),稍后可以使用其余参数(以及覆盖关键字参数)来调用它。 从而:

 >>> from functools import partial >>> foo.sample_method = partial(sample_method, foo) >>> foo.sample_method(1,2) 3 

当您考虑绑定方法是实例的部分function时,这是有道理的。

作为对象属性的未绑定函数 – 为什么这不起作用:

如果我们尝试添加sample_method的方式与我们将其添加到类中的方式相同,则它将从实例中解除绑定,并且不会将隐式self作为第一个参数。

 >>> foo.sample_method = sample_method >>> foo.sample_method(1,2) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: sample_method() takes exactly 3 arguments (2 given) 

我们可以通过显式地传递实例(或者任何东西,因为这个方法实际上不使用selfvariablesvariables)来使得未绑定的函数工作,但是它不符合其他实例的预期签名(如果我们是猴子,修补这个实例):

 >>> foo.sample_method(foo, 1, 2) 3 

放弃

请注意,只是因为这是可能的不build议。 事实上,我build议你不要这样做,除非你有一个很好的理由。 在类定义中定义正确的方法要好得多,或者不太可能直接对类进行修改,如下所示:

 Foo.sample_method = sample_method 

我认为上述答案错过了关键的一点。

让我们有一个方法的类:

 class A(object): def m(self): pass 

现在,让我们在ipython中使用它:

 In [2]: Am Out[2]: <unbound method Am> 

好的,所以m()不知何故成为A的一个无约束方法。 但是真的是这样吗?

 In [5]: A.__dict__['m'] Out[5]: <function m at 0xa66b8b4> 

事实certificate, m()只是一个函数,将其引用添加到A类字典 – 没有魔法。 那么为什么给了我们一个不受约束的方法? 这是因为这个点没有被翻译成简单的字典查找。 事实上,A .__类的调用__.__ getattribute __(A,'m'):

 In [11]: class MetaA(type): ....: def __getattribute__(self, attr_name): ....: print str(self), '-', attr_name In [12]: class A(object): ....: __metaclass__ = MetaA In [23]: Am <class '__main__.A'> - m <class '__main__.A'> - m 

现在,我不清楚为什么最后一行是打印两次,但仍然清楚发生了什么。

现在,默认的__getattribute__所做的是检查属性是否是所谓的描述符 ,即是否实现了一个特殊的__get__方法。 如果它实现了这个方法,那么返回的是调用__get__方法的结果。 回到我们Aclass的第一个版本,这就是我们所拥有的:

 In [28]: A.__dict__['m'].__get__(None, A) Out[28]: <unbound method Am> 

而且由于Python函数实现了描述符协议,如果它们是代表对象调用的,则它们将自己绑定到__get__方法中的那个对象。

好的,那么如何将一个方法添加到现有的对象呢? 假设你不介意修补课程,就像下面这样简单:

 Bm = m 

然后Bm “成为”一个无约束的方法,由于描述符魔术。

如果你想添加一个方法只是一个单一的对象,那么你必须自己模拟机器,通过使用types.MethodType:

 bm = types.MethodType(m, b) 

顺便一提:

 In [2]: Am Out[2]: <unbound method Am> In [59]: type(Am) Out[59]: <type 'instancemethod'> In [60]: type(bm) Out[60]: <type 'instancemethod'> In [61]: types.MethodType Out[61]: <type 'instancemethod'> 

在Python中,猴子补丁一般通过用自己的覆盖类或函数签名来工作。 以下是Zope Wiki的一个例子:

 from SomeOtherProduct.SomeModule import SomeClass def speak(self): return "ook ook eee eee eee!" SomeClass.speak = speak 

这段代码将覆盖/创build一个叫做讲课的方法。 在杰夫·阿特伍德最近发表的猴子补丁 。 他在C#3.0中展示了一个例子,它是我用于工作的当前语言。

至less有两种方法将方法附加到不带types.MethodType的实例。方法types.MethodType

 >>> class A: ... def m(self): ... print 'im m, invoked with: ', self >>> a = A() >>> am() im m, invoked with: <__main__.A instance at 0x973ec6c> >>> am <bound method Am of <__main__.A instance at 0x973ec6c>> >>> >>> def foo(firstargument): ... print 'im foo, invoked with: ', firstargument >>> foo <function foo at 0x978548c> 

1:

 >>> a.foo = foo.__get__(a, A) # or foo.__get__(a, type(a)) >>> a.foo() im foo, invoked with: <__main__.A instance at 0x973ec6c> >>> a.foo <bound method A.foo of <__main__.A instance at 0x973ec6c>> 

2:

 >>> instancemethod = type(Am) >>> instancemethod <type 'instancemethod'> >>> a.foo2 = instancemethod(foo, a, type(a)) >>> a.foo2() im foo, invoked with: <__main__.A instance at 0x973ec6c> >>> a.foo2 <bound method instance.foo of <__main__.A instance at 0x973ec6c>> 

有用的链接:
数据模型 – 调用描述符
描述符操作指南 – 调用描述符

我相信你正在寻找什么。 使用它在对象上设置属性。

 >>> def printme(s): print repr(s) >>> class A: pass >>> setattr(A,'printme',printme) >>> a = A() >>> a.printme() # s becomes the implicit 'self' variable < __ main __ . A instance at 0xABCDEFG> 

由于这个问题要求非Python版本,所以这里是JavaScript:

 a.methodname = function () { console.log("Yay, a new method!") } 

您可以使用lambda将方法绑定到实例:

 def run(self): print self._instanceString class A(object): def __init__(self): self._instanceString = "This is instance string" a = A() a.run = lambda: run(a) a.run() 

这是实例string

进程使用退出码0结束

Jason Pratt发布的是正确的。

 >>> class Test(object): ... def a(self): ... pass ... >>> def b(self): ... pass ... >>> Test.b = b >>> type(b) <type 'function'> >>> type(Test.a) <type 'instancemethod'> >>> type(Test.b) <type 'instancemethod'> 

正如你所看到的,Python不认为b()与()不同。 在Python中,所有的方法都只是variables而已。

整合Jason Pratt和社区wiki的答案,看看不同的绑定方法的结果:

特别注意如何将绑定函数添加为类方法,但引用范围不正确。

 #!/usr/bin/python -u import types import inspect ## dynamically adding methods to a unique instance of a class # get a list of a class's method type attributes def listattr(c): for m in [(n, v) for n, v in inspect.getmembers(c, inspect.ismethod) if isinstance(v,types.MethodType)]: print m[0], m[1] # externally bind a function as a method of an instance of a class def ADDMETHOD(c, method, name): c.__dict__[name] = types.MethodType(method, c) class C(): r = 10 # class attribute variable to test bound scope def __init__(self): pass #internally bind a function as a method of self's class -- note that this one has issues! def addmethod(self, method, name): self.__dict__[name] = types.MethodType( method, self.__class__ ) # predfined function to compare with def f0(self, x): print 'f0\tx = %d\tr = %d' % ( x, self.r) a = C() # created before modified instnace b = C() # modified instnace def f1(self, x): # bind internally print 'f1\tx = %d\tr = %d' % ( x, self.r ) def f2( self, x): # add to class instance's .__dict__ as method type print 'f2\tx = %d\tr = %d' % ( x, self.r ) def f3( self, x): # assign to class as method type print 'f3\tx = %d\tr = %d' % ( x, self.r ) def f4( self, x): # add to class instance's .__dict__ using a general function print 'f4\tx = %d\tr = %d' % ( x, self.r ) b.addmethod(f1, 'f1') b.__dict__['f2'] = types.MethodType( f2, b) b.f3 = types.MethodType( f3, b) ADDMETHOD(b, f4, 'f4') b.f0(0) # OUT: f0 x = 0 r = 10 b.f1(1) # OUT: f1 x = 1 r = 10 b.f2(2) # OUT: f2 x = 2 r = 10 b.f3(3) # OUT: f3 x = 3 r = 10 b.f4(4) # OUT: f4 x = 4 r = 10 k = 2 print 'changing br from {0} to {1}'.format(br, k) br = k print 'new br = {0}'.format(br) b.f0(0) # OUT: f0 x = 0 r = 2 b.f1(1) # OUT: f1 x = 1 r = 10 !!!!!!!!! b.f2(2) # OUT: f2 x = 2 r = 2 b.f3(3) # OUT: f3 x = 3 r = 2 b.f4(4) # OUT: f4 x = 4 r = 2 c = C() # created after modifying instance # let's have a look at each instance's method type attributes print '\nattributes of a:' listattr(a) # OUT: # attributes of a: # __init__ <bound method C.__init__ of <__main__.C instance at 0x000000000230FD88>> # addmethod <bound method C.addmethod of <__main__.C instance at 0x000000000230FD88>> # f0 <bound method C.f0 of <__main__.C instance at 0x000000000230FD88>> print '\nattributes of b:' listattr(b) # OUT: # attributes of b: # __init__ <bound method C.__init__ of <__main__.C instance at 0x000000000230FE08>> # addmethod <bound method C.addmethod of <__main__.C instance at 0x000000000230FE08>> # f0 <bound method C.f0 of <__main__.C instance at 0x000000000230FE08>> # f1 <bound method ?.f1 of <class __main__.C at 0x000000000237AB28>> # f2 <bound method ?.f2 of <__main__.C instance at 0x000000000230FE08>> # f3 <bound method ?.f3 of <__main__.C instance at 0x000000000230FE08>> # f4 <bound method ?.f4 of <__main__.C instance at 0x000000000230FE08>> print '\nattributes of c:' listattr(c) # OUT: # attributes of c: # __init__ <bound method C.__init__ of <__main__.C instance at 0x0000000002313108>> # addmethod <bound method C.addmethod of <__main__.C instance at 0x0000000002313108>> # f0 <bound method C.f0 of <__main__.C instance at 0x0000000002313108>> 

就个人而言,我更喜欢外部的ADDMETHOD函数路由,因为它允许我在迭代器中dynamic地分配新的方法名称。

 def y(self, x): pass d = C() for i in range(1,5): ADDMETHOD(d, y, 'f%d' % i) print '\nattributes of d:' listattr(d) # OUT: # attributes of d: # __init__ <bound method C.__init__ of <__main__.C instance at 0x0000000002303508>> # addmethod <bound method C.addmethod of <__main__.C instance at 0x0000000002303508>> # f0 <bound method C.f0 of <__main__.C instance at 0x0000000002303508>> # f1 <bound method ?.y of <__main__.C instance at 0x0000000002303508>> # f2 <bound method ?.y of <__main__.C instance at 0x0000000002303508>> # f3 <bound method ?.y of <__main__.C instance at 0x0000000002303508>> # f4 <bound method ?.y of <__main__.C instance at 0x0000000002303508>> 

你们应该真的看禁果 ,这是一个python库,提供支持猴子修补任何python类,甚至string。

这实际上是“Jason Pratt”的答案

虽然Jasons回答了作品,但只有在想要为课堂添加function时才有效。 当我尝试从.py源代码文件重新加载已经存在的方法时,它并不适用于我。

它花了我很长时间来find一个解决方法,但技巧似乎很简单… 1.st从源代码文件导入代码2.nd强制重新加载3.rd使用types.FunctionType(…)来转换导入和绑定方法的函数,你也可以传递当前的全局variables,因为重新加载的方法将在不同的命名空间4.th现在你可以继续按照“Jason Pratt”build议使用types.MethodType(… )

例:

 # this class resides inside ReloadCodeDemo.py class A: def bar( self ): print "bar1" def reloadCode(self, methodName): ''' use this function to reload any function of class A''' import types import ReloadCodeDemo as ReloadMod # import the code as module reload (ReloadMod) # force a reload of the module myM = getattr(ReloadMod.A,methodName) #get reloaded Method myTempFunc = types.FunctionType(# convert the method to a simple function myM.im_func.func_code, #the methods code globals(), # globals to use argdefs=myM.im_func.func_defaults # default values for variables if any ) myNewM = types.MethodType(myTempFunc,self,self.__class__) #convert the function to a method setattr(self,methodName,myNewM) # add the method to the function if __name__ == '__main__': a = A() a.bar() # now change your code and save the file a.reloadCode('bar') # reloads the file a.bar() # now executes the reloaded code 

如果可以帮忙的话,我最近发布了一个名为Gorilla的Python库,使猴子补丁的过程更加方便。

使用函数needle()来修补名为guineapig的模块如下:

 import gorilla import guineapig @gorilla.patch(guineapig) def needle(): print("awesome") 

但是它也照顾了文档中 常见问题中所示的更有趣的用例。

代码在GitHub上可用 。

这个问题是在几年前打开的,但是,有一个简单的方法来模拟一个函数与使用装饰器的类实例的绑定:

 def binder (function, instance): copy_of_function = type (function) (function.func_code, {}) copy_of_function.__bind_to__ = instance def bound_function (*args, **kwargs): return copy_of_function (copy_of_function.__bind_to__, *args, **kwargs) return bound_function class SupaClass (object): def __init__ (self): self.supaAttribute = 42 def new_method (self): print self.supaAttribute supaInstance = SupaClass () supaInstance.supMethod = binder (new_method, supaInstance) otherInstance = SupaClass () otherInstance.supaAttribute = 72 otherInstance.supMethod = binder (new_method, otherInstance) otherInstance.supMethod () supaInstance.supMethod () 

在那里,当你将函数和实例传递给binder装饰器时,它将创build一个新的函数,它具有与第一个相同的代码对象。 然后,该类的给定实例被存储在新创build的函数的属性中。 装饰器返回一个(第三个)自动调用复制函数的函数,给出实例作为第一个参数。

总之,你会得到一个模拟绑定到类实例的函数。 让原来的function不变。

我感到奇怪的是,没有人提到上面列出的所有方法都会在添加的方法和实例之间创build一个循环引用,导致对象在垃圾收集之前保持持久性。 有一个老技巧通过扩展对象的类来添加一个描述符:

 def addmethod(obj, name, func): klass = obj.__class__ subclass = type(klass.__name__, (klass,), {}) setattr(subclass, name, func) obj.__class__ = subclass 
 from types import MethodType def method(self): print 'hi!' setattr( targetObj, method.__name__, MethodType(method, targetObj, type(method)) ) 

有了这个,你可以使用自指针

我不知道Python的语法,但我知道Ruby可以做到这一点,这是微不足道的。 比方说,你想添加一个方法到数组,打印到标准输出的长度:

 class Array def print_length puts length end end 

如果您不想修改整个类,则可以将该方法添加到数组的单个实例中,而其他数组将不具有该方法:

 array = [1, 2, 3] def array.print_length puts length end 

请注意使用此function所涉及的问题。 实际上,杰夫·阿特伍德( Jeff Atwood )在不久之前写过这个消息