在 Python 中向现有对象实例添加方法
如何在 Python 中向现有对象(即不在类定义中)添加方法?
据我所知,除了某些情况外,这样做通常不被认为是良好做法。
How do I add a method to an existing object (i.e., not in the class definition) in Python?
I understand that it's not generally considered good practice to do so, except in some cases.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(19)
在 Python 中,函数和绑定方法之间存在差异。
绑定方法已“绑定”(如何描述)到实例,并且每当调用该方法时,该实例都将作为第一个参数传递。
不过,作为类的属性(而不是实例)的可调用对象仍然未绑定,因此您可以随时修改类定义:
以前定义的实例也会更新(只要它们没有覆盖属性本身) ):
当您想要将方法附加到单个实例时,问题就出现了:
当函数直接附加到实例时,不会自动绑定:
要绑定它,我们可以使用 types 模块中的 MethodType 函数:
这次该类的其他实例有未受到影响:
可以通过阅读有关 描述符 和 元类 编程。
In Python, there is a difference between functions and bound methods.
Bound methods have been "bound" (how descriptive) to an instance, and that instance will be passed as the first argument whenever the method is called.
Callables that are attributes of a class (as opposed to an instance) are still unbound, though, so you can modify the class definition whenever you want:
Previously defined instances are updated as well (as long as they haven't overridden the attribute themselves):
The problem comes when you want to attach a method to a single instance:
The function is not automatically bound when it's attached directly to an instance:
To bind it, we can use the MethodType function in the types module:
This time other instances of the class have not been affected:
More information can be found by reading about descriptors and metaclass programming.
前言 - 关于兼容性的说明:其他答案可能只适用于 Python 2 - 这个答案应该在 Python 2 和 3 中工作得很好。如果只编写 Python 3,您可能会省略显式继承
object
,但除此之外,代码应保持不变。是的,这是可能的 - 但不推荐
我不推荐这样做。这是一个坏主意。不要这样做。
原因如下:
因此,我建议您不要这样做,除非您有充分的理由。 最好在类定义中定义正确的方法,或者更少最好直接对类进行猴子修补,如下所示:
因为它很有启发性,但是,我将向您展示一些执行此操作的方法。
如何做到
这里有一些设置代码。我们需要一个类定义。可以进口,但没关系。
创建一个实例:
创建一个方法来添加到它:
Method naught (0) - 使用描述符方法,
__get__
函数上的点式查找调用该函数的
__get__
方法实例,将对象绑定到方法,从而创建“绑定方法”。现在:
方法一 - types.MethodType
首先,导入类型,从中我们将获得方法构造函数:
现在我们将方法添加到实例中。为此,我们需要来自
types
模块(我们在上面导入的)的 MethodType 构造函数。types.MethodType 的参数签名(在 Python 3 中)是
(function, instance)
:用法:
顺便说一句,在 Python 2 中签名是
(function, instance, class)
>:方法二:词法绑定
首先,我们创建一个包装函数,将方法绑定到实例:
用法:
方法三:functools.partial
部分函数将第一个参数应用于函数(以及可选的关键字参数),稍后可以调用与其余参数(以及覆盖关键字参数)。因此:
当您认为绑定方法是实例的部分函数时,这是有道理的。
未绑定函数作为对象属性 - 为什么这不起作用:
如果我们尝试以与将其添加到类中相同的方式添加sample_method,它将与实例解除绑定,并且不会将隐式 self 作为第一个论点。
我们可以通过显式传递实例(或任何东西,因为此方法实际上并不使用 self 参数变量)来使未绑定函数工作,但它与其他实例的预期签名不一致(如果我们要对这个实例进行猴子修补):
结论
您现在知道可以通过多种方法来执行此操作,但严肃地说 - 不要执行此操作。
Preface - a note on compatibility: other answers may only work in Python 2 - this answer should work perfectly well in Python 2 and 3. If writing Python 3 only, you might leave out explicitly inheriting from
object
, but otherwise the code should remain the same.Yes, it is possible - But not recommended
I don't recommend this. This is a bad idea. Don't do it.
Here's a couple of reasons:
Thus, I suggest that you not do this unless you have a really good reason. It is far better to define the correct method in the class definition or less preferably to monkey-patch the class directly, like this:
Since it's instructive, however, I'm going to show you some ways of doing this.
How it can be done
Here's some setup code. We need a class definition. It could be imported, but it really doesn't matter.
Create an instance:
Create a method to add to it:
Method nought (0) - use the descriptor method,
__get__
Dotted lookups on functions call the
__get__
method of the function with the instance, binding the object to the method and thus creating a "bound method."and now:
Method one - types.MethodType
First, import types, from which we'll get the method constructor:
Now we add the method to the instance. To do this, we require the MethodType constructor from the
types
module (which we imported above).The argument signature for types.MethodType (in Python 3) is
(function, instance)
:and usage:
Parenthetically, in Python 2 the signature was
(function, instance, class)
:Method two: lexical binding
First, we create a wrapper function that binds the method to the instance:
usage:
Method three: functools.partial
A partial function applies the first argument(s) to a function (and optionally keyword arguments), and can later be called with the remaining arguments (and overriding keyword arguments). Thus:
This makes sense when you consider that bound methods are partial functions of the instance.
Unbound function as an object attribute - why this doesn't work:
If we try to add the sample_method in the same way as we might add it to the class, it is unbound from the instance, and doesn't take the implicit self as the first argument.
We can make the unbound function work by explicitly passing the instance (or anything, since this method doesn't actually use the
self
argument variable), but it would not be consistent with the expected signature of other instances (if we're monkey-patching this instance):Conclusion
You now know several ways you could do this, but in all seriousness - don't do this.
模块 new 自 python 2.6 起已弃用,并在 3.0 中删除,使用 types
参见 http://docs.python.org/library/new.html
在下面的示例中,我故意从
patch_me()
函数中删除了返回值。我认为给出返回值可能会让人相信 patch 返回一个新对象,但事实并非如此 - 它修改了传入的对象。也许这可以促进更严格地使用猴子补丁。
Module new is deprecated since python 2.6 and removed in 3.0, use types
see http://docs.python.org/library/new.html
In the example below I've deliberately removed return value from
patch_me()
function.I think that giving return value may make one believe that patch returns a new object, which is not true - it modifies the incoming one. Probably this can facilitate a more disciplined use of monkeypatching.
我觉得上面的回答都没有抓住重点。
让我们有一个带有方法的类:
现在,让我们在 ipython 中使用它:
好的,所以 m() 不知何故成为 A 的未绑定方法。但真的是这样吗?
事实证明,m() 只是一个函数,对其的引用被添加到 A 类字典中 - 没有什么魔法。那为什么Am给我们一个未绑定的方法呢?这是因为点没有转换为简单的字典查找。它实际上是对 A.__class__.__getattribute__(A, 'm'): 的调用:
现在,我完全不确定为什么最后一行被打印两次,但仍然很清楚那里发生了什么。
现在,默认的 __getattribute__ 的作用是检查该属性是否是所谓的 描述符与否,即它是否实现了特殊的 __get__ 方法。如果它实现了该方法,那么返回的就是调用该 __get__ 方法的结果。回到我们的 A 类的第一个版本,这就是我们所拥有的:
并且因为 Python 函数实现了描述符协议,所以如果代表一个对象调用它们,它们会将自己绑定到该对象他们的 __get__ 方法。
好的,那么如何向现有对象添加方法呢?假设您不介意修补类,它就像这样简单:
然后 Bm “成为”一个未绑定的方法,这要归功于描述符的魔力。
如果您只想向单个对象添加方法,那么您必须使用 types.MethodType 自己模拟机器:
顺便说一句:
I think that the above answers missed the key point.
Let's have a class with a method:
Now, let's play with it in ipython:
Ok, so m() somehow becomes an unbound method of A. But is it really like that?
It turns out that m() is just a function, reference to which is added to A class dictionary - there's no magic. Then why A.m gives us an unbound method? It's because the dot is not translated to a simple dictionary lookup. It's de facto a call of A.__class__.__getattribute__(A, 'm'):
Now, I'm not sure out of the top of my head why the last line is printed twice, but still it's clear what's going on there.
Now, what the default __getattribute__ does is that it checks if the attribute is a so-called descriptor or not, i.e. if it implements a special __get__ method. If it implements that method, then what is returned is the result of calling that __get__ method. Going back to the first version of our A class, this is what we have:
And because Python functions implement the descriptor protocol, if they are called on behalf of an object, they bind themselves to that object in their __get__ method.
Ok, so how to add a method to an existing object? Assuming you don't mind patching class, it's as simple as:
Then B.m "becomes" an unbound method, thanks to the descriptor magic.
And if you want to add a method just to a single object, then you have to emulate the machinery yourself, by using types.MethodType:
By the way:
在 Python 中,monkeypatching 通常通过用您自己的签名覆盖类或函数的签名来工作。以下是 Zope Wiki< 的示例/a>:
此代码将覆盖/创建类中名为
speak
的方法。在 Jeff Atwood 的最近关于猴子补丁的帖子中,他展示了 C# 3.0 中的一个示例:我当前工作使用的语言。In Python monkeypatching generally works by overwriting a class or function's signature with your own. Below is an example from the Zope Wiki:
This code will overwrite/create a method called
speak
in the class. In Jeff Atwood's recent post on monkey patching, he showed an example in C# 3.0 which is the current language I use for work.您可以使用 lambda 将方法绑定到实例:
输出:
You can use lambda to bind a method to an instance:
Output:
我相信您正在寻找的是
setattr
。使用它来设置对象的属性。
What you're looking for is
setattr
I believe.Use this to set an attribute on an object.
至少有两种方法可以将方法附加到没有
types.MethodType
的实例:1:
2:
有用的链接:
数据模型 - 调用描述符
描述符操作指南 - 调用描述符
There are at least two ways for attach a method to an instance without
types.MethodType
:1:
2:
Useful links:
Data model - invoking descriptors
Descriptor HowTo Guide - invoking descriptors
合并 Jason Pratt 和社区 wiki 的答案,看看不同绑定方法的结果:
特别注意如何将绑定函数添加为类方法有效,但引用范围不正确。
就我个人而言,我更喜欢外部 ADDMETHOD 函数路线,因为它也允许我在迭代器中动态分配新方法名称。
Consolidating Jason Pratt's and the community wiki answers, with a look at the results of different methods of binding:
Especially note how adding the binding function as a class method works, but the referencing scope is incorrect.
Personally, I prefer the external ADDMETHOD function route, as it allows me to dynamically assign new method names within an iterator as well.
由于这个问题要求非 Python 版本,因此这里是 JavaScript:
Since this question asked for non-Python versions, here's JavaScript:
这实际上是“Jason Pratt”答案的一个插件
,尽管 Jason 的答案有效,但只有在想向类添加函数时它才有效。
当我尝试从 .py 源代码文件重新加载已经存在的方法时,它对我不起作用。
我花了很长时间才找到解决方法,但技巧似乎很简单......
1.st从源代码文件中导入代码
2.强制重新加载
3.rd使用types.FunctionType(...)将导入并绑定的方法转换为函数
您还可以传递当前的全局变量,因为重新加载的方法将位于不同的命名空间中
4.现在您可以按照“Jason Pratt”的建议继续
使用 types.MethodType(...)
示例:
This is actually an addon to the answer of "Jason Pratt"
Although Jasons answer works, it does only work if one wants to add a function to a class.
It did not work for me when I tried to reload an already existing method from the .py source code file.
It took me for ages to find a workaround, but the trick seems simple...
1.st import the code from the source code file
2.nd force a reload
3.rd use types.FunctionType(...) to convert the imported and bound method to a function
you can also pass on the current global variables, as the reloaded method would be in a different namespace
4.th now you can continue as suggested by "Jason Pratt"
using the types.MethodType(...)
Example:
这个问题是几年前提出的,但是嘿,有一种简单的方法可以使用装饰器模拟函数到类实例的绑定:
在那里,当您将函数和实例传递给绑定器装饰器时,它将创建一个新函数,具有与第一个相同的代码对象。然后,该类的给定实例存储在新创建的函数的属性中。装饰器返回一个(第三个)函数,自动调用复制的函数,并将实例作为第一个参数。
总之,您得到一个模拟它绑定到类实例的函数。保持原来的功能不变。
This question was opened years ago, but hey, there's an easy way to simulate the binding of a function to a class instance using decorators:
There, when you pass the function and the instance to the binder decorator, it will create a new function, with the same code object as the first one. Then, the given instance of the class is stored in an attribute of the newly created function. The decorator return a (third) function calling automatically the copied function, giving the instance as the first parameter.
In conclusion you get a function simulating it's binding to the class instance. Letting the original function unchanged.
我觉得奇怪的是,没有人提到上面列出的所有方法都会在添加的方法和实例之间创建循环引用,导致对象在垃圾回收之前一直保持不变。有一个老技巧通过扩展对象的类来添加描述符:
I find it strange that nobody mentioned that all of the methods listed above creates a cycle reference between the added method and the instance, causing the object to be persistent till garbage collection. There was an old trick adding a descriptor by extending the class of the object:
如果有什么帮助的话,我最近发布了一个名为 Gorilla 的 Python 库,以使猴子修补过程更加方便。
使用函数
needle()
来修补名为guineapig
的模块,如下所示:但它还可以处理更有趣的用例,如 常见问题解答来自
该代码可在 GitHub 上获取。
If it can be of any help, I recently released a Python library named Gorilla to make the process of monkey patching more convenient.
Using a function
needle()
to patch a module namedguineapig
goes as follows:But it also takes care of more interesting use cases as shown in the FAQ from the documentation.
The code is available on GitHub.
这样,您就可以使用 self 指针
With this, you can use the self pointer
杰森普拉特发布的内容是正确的。
正如您所看到的,Python 认为 b() 与 a() 没有任何不同。在 Python 中,所有方法都只是恰好是函数的变量。
What Jason Pratt posted is correct.
As you can see, Python doesn't consider b() any different than a(). In Python all methods are just variables that happen to be functions.
如何从类的实例恢复类
How to recover a class from an instance of a class
除了其他人所说的之外,我发现
__repr__
和__str__
方法不能在对象级别上进行猴子修补,因为repr()
和str()
使用类方法,而不是本地边界对象方法:Apart from what others said, I found that
__repr__
and__str__
methods can't be monkeypatched on object level, becauserepr()
andstr()
use class-methods, not locally-bounded object methods:感谢阿图罗!
你的回答让我走上了正轨!
基于Arturo的代码,我写了一个小类:
这个类允许您随时添加新的属性和方法。
编辑:
这是一个更通用的解决方案:
让我们测试一下:
Thanks to Arturo!
Your answer got me on the right track!
Based on Arturo's code, I wrote a little class:
This class allows you to add new attributes and methods at any time.
Edit:
Here is a more generalized solution:
Let's test it: