我已经了解到,可以在Python中向现有对象(即,不在类定义中)添加方法。
我明白这样做并不总是好的。但你怎么能做到这一点呢?
我已经了解到,可以在Python中向现有对象(即,不在类定义中)添加方法。
我明白这样做并不总是好的。但你怎么能做到这一点呢?
当前回答
前言-关于兼容性的说明:其他答案可能只在Python2-这个答案应该在Python2和3中运行得很好。如果只编写Python3,您可能会忽略显式继承自对象,否则代码应该保持不变。
向现有对象实例添加方法我已经了解到,可以在Python中向现有对象(例如,不在类定义中)添加方法。我知道这样做并不总是一个好的决定。但是,一个人怎么做呢?
是的,有可能-但不建议
我不建议这样做。这是个坏主意。不要这样做。
以下是几个原因:
您将为每个执行此操作的实例添加一个绑定对象。如果经常这样做,可能会浪费大量内存。绑定方法通常只在其调用的短时间内创建,然后在自动垃圾收集时停止存在。如果手动执行此操作,则将有一个引用绑定方法的名称绑定,这将防止其在使用时进行垃圾收集。给定类型的对象实例通常在该类型的所有对象上都有其方法。如果在其他地方添加方法,某些实例将具有这些方法,而其他实例则没有。程序员不会预料到这一点,你可能会违反最不意外的规则。由于有其他真正好的理由不这样做,如果你这样做,你还会给自己带来坏名声。
因此,我建议你不要这样做,除非你有充分的理由。在类定义中定义正确的方法要好得多,或者直接对类进行猴式修补,如下所示:
Foo.sample_method = sample_method
不过,既然这很有启发性,我将向你展示一些这样做的方法。
如何做到这一点
这是一些设置代码。我们需要一个类定义。它可以进口,但真的没关系。
class Foo(object):
'''An empty class to demonstrate adding a method to an instance'''
创建实例:
foo = Foo()
创建要添加到其中的方法:
def sample_method(self, bar, baz):
print(bar + baz)
方法零(0)-使用描述符方法__get__
函数上的点查找使用实例调用函数的__get__方法,将对象绑定到方法,从而创建“绑定方法”
foo.sample_method = sample_method.__get__(foo)
现在:
>>> foo.sample_method(1,2)
3
方法一-types.MethodType
首先,导入类型,我们将从中获取方法构造函数:
import types
现在我们将该方法添加到实例中。为此,我们需要类型模块中的MethodType构造函数(我们在上面导入了它)。
types.MethodType(在Python 3中)的参数签名是(function,instance):
foo.sample_method = types.MethodType(sample_method, foo)
和用法:
>>> foo.sample_method(1,2)
3
附带地,在Python 2中,签名是(函数、实例、类):
foo.sample_method = types.MethodType(sample_method, foo, Foo)
方法二:词汇绑定
首先,我们创建一个将方法绑定到实例的包装函数:
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
分部函数将第一个参数应用于函数(以及可选的关键字参数),然后可以用剩余的参数(以及重写关键字参数)调用。因此:
>>> from functools import partial
>>> foo.sample_method = partial(sample_method, foo)
>>> foo.sample_method(1,2)
3
当您认为绑定方法是实例的部分函数时,这是有意义的。
作为对象属性的未绑定函数-为什么不起作用:
如果我们尝试以与将sample_method添加到类中相同的方式添加sample_methods,它将与实例绑定,并且不会将隐式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)
我们可以通过显式传递实例(或任何东西,因为此方法实际上不使用自参数变量)来使未绑定函数工作,但它与其他实例的预期签名不一致(如果我们正在对该实例进行猴子修补):
>>> foo.sample_method(foo, 1, 2)
3
结论
你现在知道有几种方法可以做到这一点,但认真地说,不要这样做。
其他回答
自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'>
前言-关于兼容性的说明:其他答案可能只在Python2-这个答案应该在Python2和3中运行得很好。如果只编写Python3,您可能会忽略显式继承自对象,否则代码应该保持不变。
向现有对象实例添加方法我已经了解到,可以在Python中向现有对象(例如,不在类定义中)添加方法。我知道这样做并不总是一个好的决定。但是,一个人怎么做呢?
是的,有可能-但不建议
我不建议这样做。这是个坏主意。不要这样做。
以下是几个原因:
您将为每个执行此操作的实例添加一个绑定对象。如果经常这样做,可能会浪费大量内存。绑定方法通常只在其调用的短时间内创建,然后在自动垃圾收集时停止存在。如果手动执行此操作,则将有一个引用绑定方法的名称绑定,这将防止其在使用时进行垃圾收集。给定类型的对象实例通常在该类型的所有对象上都有其方法。如果在其他地方添加方法,某些实例将具有这些方法,而其他实例则没有。程序员不会预料到这一点,你可能会违反最不意外的规则。由于有其他真正好的理由不这样做,如果你这样做,你还会给自己带来坏名声。
因此,我建议你不要这样做,除非你有充分的理由。在类定义中定义正确的方法要好得多,或者直接对类进行猴式修补,如下所示:
Foo.sample_method = sample_method
不过,既然这很有启发性,我将向你展示一些这样做的方法。
如何做到这一点
这是一些设置代码。我们需要一个类定义。它可以进口,但真的没关系。
class Foo(object):
'''An empty class to demonstrate adding a method to an instance'''
创建实例:
foo = Foo()
创建要添加到其中的方法:
def sample_method(self, bar, baz):
print(bar + baz)
方法零(0)-使用描述符方法__get__
函数上的点查找使用实例调用函数的__get__方法,将对象绑定到方法,从而创建“绑定方法”
foo.sample_method = sample_method.__get__(foo)
现在:
>>> foo.sample_method(1,2)
3
方法一-types.MethodType
首先,导入类型,我们将从中获取方法构造函数:
import types
现在我们将该方法添加到实例中。为此,我们需要类型模块中的MethodType构造函数(我们在上面导入了它)。
types.MethodType(在Python 3中)的参数签名是(function,instance):
foo.sample_method = types.MethodType(sample_method, foo)
和用法:
>>> foo.sample_method(1,2)
3
附带地,在Python 2中,签名是(函数、实例、类):
foo.sample_method = types.MethodType(sample_method, foo, Foo)
方法二:词汇绑定
首先,我们创建一个将方法绑定到实例的包装函数:
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
分部函数将第一个参数应用于函数(以及可选的关键字参数),然后可以用剩余的参数(以及重写关键字参数)调用。因此:
>>> from functools import partial
>>> foo.sample_method = partial(sample_method, foo)
>>> foo.sample_method(1,2)
3
当您认为绑定方法是实例的部分函数时,这是有意义的。
作为对象属性的未绑定函数-为什么不起作用:
如果我们尝试以与将sample_method添加到类中相同的方式添加sample_methods,它将与实例绑定,并且不会将隐式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)
我们可以通过显式传递实例(或任何东西,因为此方法实际上不使用自参数变量)来使未绑定函数工作,但它与其他实例的预期签名不一致(如果我们正在对该实例进行猴子修补):
>>> foo.sample_method(foo, 1, 2)
3
结论
你现在知道有几种方法可以做到这一点,但认真地说,不要这样做。
除了其他人所说的,我发现__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'
我相信你要找的是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中,所有方法都只是恰好是函数的变量。