我已经了解到,可以在Python中向现有对象(即,不在类定义中)添加方法。

我明白这样做并不总是好的。但你怎么能做到这一点呢?


当前回答

from types import MethodType

def method(self):
   print 'hi!'


setattr( targetObj, method.__name__, MethodType(method, targetObj, type(method)) )

使用此选项,可以使用self指针

其他回答

这个问题早在几年前就提出了,但嘿,有一种简单的方法可以使用decorator模拟函数与类实例的绑定:

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

在那里,当您将函数和实例传递给绑定器装饰器时,它将创建一个新函数,其代码对象与第一个相同。然后,类的给定实例存储在新创建的函数的属性中。装饰器返回一个(第三个)函数,自动调用复制的函数,将实例作为第一个参数。最后,您将得到一个函数,模拟它与类实例的绑定。保持原始函数不变。

可以使用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()

输出:

This is instance string

在Python中,猴痘通常通过用自己的签名覆盖类或函数的签名来工作。以下是Zope Wiki的示例:

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

此代码将覆盖/创建类中名为speak的方法。在Jeff Atwood最近发表的关于猴子修补的文章中,他展示了一个C#3.0的例子,这是我当前工作中使用的语言。

这实际上是对“杰森·普拉特”答案的补充

虽然Jasons的答案是有效的,但它只在想要向类中添加函数时有效。当我试图从.py源代码文件重新加载一个已经存在的方法时,它对我来说并不起作用。

我花了很长时间才找到解决方法,但技巧似乎很简单。。。1.从源代码文件导入代码2.强制重新加载3.rd使用types.FunctionType(…)将导入和绑定的方法转换为函数您还可以传递当前的全局变量,因为重新加载的方法将位于不同的命名空间中4.现在你可以按照“杰森·普拉特”的建议继续使用类型.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

我认为上述答案没有抓住重点。

让我们用一个方法来上课:

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

现在,让我们在ipython中玩一下:

In [2]: A.m
Out[2]: <unbound method A.m>

好的,所以m()在某种程度上变成了A的非绑定方法,但它真的是这样吗?

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

事实证明,m()只是一个函数,对它的引用被添加到a类字典中——这没有什么魔力。那为什么A.m会给我们一个未绑定的方法?这是因为点没有被翻译成简单的字典查找。这实际上是对a.__class__.__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]: A.m
<class '__main__.A'> - m
<class '__main__.A'> - m

现在,我不清楚为什么最后一行要打印两次,但仍然很清楚是怎么回事。

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

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

因为Python函数实现了描述符协议,所以如果代表对象调用它们,它们会在__get__方法中将自己绑定到该对象。

好的,那么如何向现有对象添加方法呢?假设您不介意修补类,那么简单如下:

B.m = m

然后,由于描述符的魔力,B.m“成为”一个未绑定的方法。

如果你想将一个方法添加到一个对象中,那么你必须自己使用types.MethodType来模拟机器:

b.m = types.MethodType(m, b)

顺便说一句:

In [2]: A.m
Out[2]: <unbound method A.m>

In [59]: type(A.m)
Out[59]: <type 'instancemethod'>

In [60]: type(b.m)
Out[60]: <type 'instancemethod'>

In [61]: types.MethodType
Out[61]: <type 'instancemethod'>