Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 向现有对象实例添加方法_Python_Oop_Methods_Monkeypatching - Fatal编程技术网

Python 向现有对象实例添加方法

Python 向现有对象实例添加方法,python,oop,methods,monkeypatching,Python,Oop,Methods,Monkeypatching,我已经读到,可以将方法添加到现有对象中,即不在Python的类定义中 我知道这样做并不总是好的。但是如何做到这一点呢?在Python中,monkeypatching通常通过用自己的签名覆盖类或函数的签名来工作。以下是一个来自以下方面的示例: 此代码将覆盖/创建类中名为peak的方法。在Jeff Atwood的文章中,他展示了一个C 3.0中的示例,这是我目前用于工作的语言。在Python中,函数和绑定方法之间存在差异 >>> def foo(): ... print &

我已经读到,可以将方法添加到现有对象中,即不在Python的类定义中


我知道这样做并不总是好的。但是如何做到这一点呢?

在Python中,monkeypatching通常通过用自己的签名覆盖类或函数的签名来工作。以下是一个来自以下方面的示例:


此代码将覆盖/创建类中名为peak的方法。在Jeff Atwood的文章中,他展示了一个C 3.0中的示例,这是我目前用于工作的语言。

在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>>
>>>
如果要将方法附加到单个实例,则会出现问题:

>>> 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>
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()
foo = Foo()
def bind(instance, method):
    def binding_scope_fn(*args, **kwargs): 
        return method(instance, *args, **kwargs)
    return binding_scope_fn
>>> foo.sample_method(foo, 1, 2)
3
直接连接到实例时,函数不会自动绑定:

>>> 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>
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()
foo = Foo()
def bind(instance, method):
    def binding_scope_fn(*args, **kwargs): 
        return method(instance, *args, **kwargs)
    return binding_scope_fn
>>> foo.sample_method(foo, 1, 2)
3
要绑定它,我们可以使用:

这一次,该类的其他实例没有受到影响:

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

通过阅读和可以找到更多信息。

我相信您正在寻找的是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>

杰森·普拉特的帖子是正确的

>>> 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与A有任何不同。在Python中,所有方法都只是碰巧是函数的变量

模块new自python 2.6以来已被弃用,并在3.0中删除,请使用类型

在下面的示例中,我故意删除了patch_me函数的返回值。 我认为,给出返回值可能会让人相信patch返回了一个新对象,这是不正确的——它修改了传入的对象。这可能有助于更严格地使用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'>

我认为上面的答案没有抓住要点

让我们创建一个具有方法的类:

class A(object):
    def m(self):
        pass
现在,让我们在ipython中玩它:

In [2]: A.m
Out[2]: <unbound method A.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'>

整合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 b.r from {0} to {1}'.format(b.r, k)
b.r = k
print 'new b.r = {0}'.format(b.r)

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函数路由,因为它允许我在迭代器中动态地分配新的方法名

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版本的,所以下面是JavaScript:

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

将方法附加到没有类型的实例至少有两种方法。MethodType:

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

2:

有用链接:

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

使用功能针对名为guineapig的模块进行补片,步骤如下:

import gorilla
import guineapig
@gorilla.patch(guineapig)
def needle():
    print("awesome")
但它也处理了更多有趣的用例,如来自的中所示


代码在上提供。

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

>>> 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>
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()
foo = Foo()
def bind(instance, method):
    def binding_scope_fn(*args, **kwargs): 
        return method(instance, *args, **kwargs)
    return binding_scope_fn
>>> foo.sample_method(foo, 1, 2)
3
输出:

This is instance string

前言-关于兼容性的说明:其他答案可能只适用于Python2-此答案在Python2和Python3中应该非常有效。如果只编写Python3,您可能会忽略显式继承自对象,但否则代码应该保持不变

向现有对象实例添加方法 我已经读到,可以将方法添加到现有对象中,例如,不在Python的类定义中

我知道这样做并不总是一个好的决定。但是,怎么可能做到这一点呢

是的,这是可能的,但不推荐 我不推荐这个。这是个坏主意。不要这样做

以下是几个原因:

您将向每个实例添加一个绑定对象。如果你经常这样做,你可能会浪费很多内存。绑定方法通常只在其调用的较短时间内创建,然后在自动垃圾回收时它们就不存在了。如果手动执行此操作,则会有一个名称绑定引用绑定方法-这将防止其在使用时进行垃圾收集。 给定类型的对象实例通常在该类型的所有对象上都有其方法。如果在其他地方添加方法,某些实例将具有这些方法,而其他实例则不会。程序员不会预料到这一点,您可能会违反。 因为还有其他很好的理由不这样做,如果你这样做,你会给自己一个很差的声誉。 因此,我建议你不要这样做,除非你有很好的理由。最好在类定义中定义正确的方法,或者直接对类进行猴子补丁,如下所示:

Foo.sample_method = sample_method
但是,由于它很有启发性,我将向您展示一些方法

如何做到这一点 这里有一些设置代码。我们需要一个类定义。它可以进口,但这真的不重要

class Foo(object):
    '''An empty class to demonstrate adding a method to an instance'''
创建一个实例:

>>> 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>
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()
foo = Foo()
def bind(instance, method):
    def binding_scope_fn(*args, **kwargs): 
        return method(instance, *args, **kwargs)
    return binding_scope_fn
>>> foo.sample_method(foo, 1, 2)
3
创建要添加到其中的方法:

def sample_method(self, bar, baz):
    print(bar + baz)
方法nou ght 0-使用描述符方法__ 函数上的虚线查找使用实例调用函数的_get _uu方法,将对象绑定到方法,从而创建绑定方法

foo.sample_method = sample_method.__get__(foo)
现在:

>>> foo.sample_method(1,2)
3
方法一-types.MethodType 首先,导入类型,我们将从中获得方法构造函数:

import types
现在我们将该方法添加到实例中。为此,我们需要前面导入的types模块中的MethodType构造函数

types.MethodType的参数签名为函数、实例、类:

使用方法:

>>> foo.sample_method(1,2)
3
方法二:词汇绑定 首先,我们创建一个包装函数,将方法绑定到实例:

>>> 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>
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()
foo = Foo()
def bind(instance, method):
    def binding_scope_fn(*args, **kwargs): 
        return method(instance, *args, **kwargs)
    return binding_scope_fn
>>> foo.sample_method(foo, 1, 2)
3
用法:

>>> foo.sample_method = bind(foo, sample_method)    
>>> foo.sample_method(1,2)
3
方法三:functools.partial 分部函数将第一个参数应用于函数和可选的关键字参数,以后可以使用剩余参数和重写关键字参数调用。因此:

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

当考虑绑定方法是实例的部分函数时,这是有意义的。

作为对象属性的未绑定函数-这不起作用的原因: 如果我们尝试以与将sample_方法添加到类中相同的方式添加sample_方法,那么它将与实例解除绑定,并且不会将隐式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)
结论 你现在知道了几种方法可以做到这一点,但严肃地说,不要这样做。

这实际上是对Jason Pratt答案的补充 虽然JasonsAnswer是有效的,但它只在需要向类中添加函数时才有效。 当我试图从.py源代码文件重新加载一个已经存在的方法时,它对我不起作用

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

这个问题在几年前就已经提出了,但是有一种简单的方法可以使用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 ()
在那里,当您将函数和实例传递给binder decorator时,它将创建一个新函数,其代码对象与第一个相同。然后,类的给定实例存储在新创建的函数的属性中。装饰器返回第三个函数,自动调用复制的函数,将实例作为第一个参数。
总之,您将得到一个模拟其绑定到类实例的函数。让原始函数保持不变。

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

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

这样,您就可以使用自指针了。除了其他人所说的之外,我发现_urepr_uu和_str_u方法不能在对象级别上进行修补,因为repr和str使用的是类方法,而不是局部绑定的对象方法:

# Instance monkeypatch
[ins] In [55]: x.__str__ = show.__get__(x)                                                                 

[ins] In [56]: x                                                                                           
Out[56]: <__main__.X at 0x7fc207180c10>

[ins] In [57]: str(x)                                                                                      
Out[57]: '<__main__.X object at 0x7fc207180c10>'

[ins] In [58]: x.__str__()                                                                                 
Nice object!

# Class monkeypatch
[ins] In [62]: X.__str__ = lambda _: "From class"                                                          

[ins] In [63]: str(x)                                                                                      
Out[63]: 'From class'

这是修补类A,而不是实例A。是否有理由使用setattrA、'printme',printme而不是简单地使用A.printme=printme?如果在运行时构造方法名,这是有意义的。您修补的是类测试,而不是它的实例。您正在向类添加方法,不是对象实例。但它会影响类的所有实例,而不仅仅是一个。addmethod以以下方式重写def addmethodself,method,name:self.\uuuu dict\uuu[name]=types.MethodType method,self解决问题,而不是手动创建MethodType,手动调用并让函数生成您的实例:barFighters.\uu get\uu a为绑定到a的barFighters生成一个绑定方法。@MartijnPieters使用描述符协议与创建方法类型相比,除了可读性稍高之外,还有哪些优点。@enderNAPM:几个:它更有可能继续完全相同的工作正如访问实例上的属性所做的那样。它也适用于classmethod和staticmethod以及其他描述符。它避免了用另一个导入混淆名称空间。建议的描述符方法的完整代码是a.barFighters=barFighters。请注意,python3中没有未绑定方法,因为函数和未绑定方法之间的差异非常小,Python3消除了这种区别。免责声明正是我想知道的。方法定义只是嵌套在类定义中的函数。@Atcold我在介绍中详细介绍了避免这样做的原因。uu get_u方法还需要类作为下一个参数:sample_方法。u get_ufoo,foo。@AidasBendoraitis我不认为它需要它,它是应用描述符时提供的可选参数
或者协议-但是python函数不使用参数:很好的解释和方法。如果要添加的方法需要引用self,这将如何工作?我的第一次尝试导致语法错误,但在方法的定义中添加self似乎不起作用。导入类型类AOObject:但似乎也适用于旧式对象ax='ax'传递def patch_metarget:def methodtarget,x:print self.ax print x=,x print调用自,target target.method=types.MethodTypemethod,target如果需要添加更多a=a printa.ax