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

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


当前回答

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

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

其他回答

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

如果有什么帮助的话,我最近发布了一个名为Gorilla的Python库,以使猴子修补过程更加方便。

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

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

但它还处理了文档中常见问题解答中所示的更有趣的用例。

该代码在GitHub上可用。

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

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

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

def addmethod(obj, name, func):
    klass = obj.__class__
    subclass = type(klass.__name__, (klass,), {})
    setattr(subclass, name, func)
    obj.__class__ = subclass

我相信你要找的是setattr。使用此设置对象的属性。

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