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

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


当前回答

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

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

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

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

该代码在GitHub上可用。

其他回答

在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的例子,这是我当前工作中使用的语言。

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

看见http://docs.python.org/library/new.html

在下面的示例中,我故意从patch_me()函数中删除了返回值。我认为,给出返回值可能会让人相信补丁会返回一个新对象,这是不正确的——它会修改传入的对象。这可能有助于更严格地使用猴痘。

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

除了其他人所说的,我发现__repr_和__str__方法不能在对象级别上进行猴痘,因为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'

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

>>> class A:
...  def m(self):
...   print 'im m, invoked with: ', self

>>> a = A()
>>> a.m()
im m, invoked with:  <__main__.A instance at 0x973ec6c>
>>> a.m
<bound method A.m 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(A.m)
>>> 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>>

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

如何从类的实例恢复类

class UnderWater:
    def __init__(self):
        self.net = 'underwater'

marine = UnderWater() # Instantiate the class

# Recover the class from the instance and add attributes to it.
class SubMarine(marine.__class__):  
    def __init__(self):
        super().__init__()
            self.sound = 'Sonar'
    
print(SubMarine, SubMarine.__name__, SubMarine().net, SubMarine().sound)

# Output
# (__main__.SubMarine,'SubMarine', 'underwater', 'Sonar')