我读过,可以在Python中向现有对象(即不在类定义中)添加方法。

我了解这样做并不总是一件好事。 但是怎么可能呢?


#1楼

至少有两种方法可以将方法附加到没有types.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>>

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


#2楼

Jason Pratt发表的内容是正确的。

>>> 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中,所有方法都只是碰巧是函数的变量。


#3楼

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

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

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

但是,它还会处理一些更有趣的用例,如文档 FAQ中所示。

该代码可在GitHub上获得 。


#4楼

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

def run(self):print self._instanceStringclass A(object):def __init__(self):self._instanceString = "This is instance string"a = A()
a.run = lambda: run(a)
a.run()

输出:

This is instance string

#5楼

前言-有关兼容性的说明:其他答案可能仅在Python 2中有效-此答案在Python 2和3中应该可以很好地工作。如果仅编写Python 3,则可能会显式地从object继承,否则代码应保留相同。

向现有对象实例添加方法

我读过,可以在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

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

types.MethodType的参数签名为(function, instance, class)

foo.sample_method = types.MethodType(sample_method, foo, Foo)

和用法:

>>> foo.sample_method(1,2)
3

方法二:词法绑定

首先,我们创建一个包装器函数,将方法绑定到实例:

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,则它不受实例约束,并且不会将隐式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)

我们可以通过显式传递实例(或其他任何方法,因为此方法实际上并不使用self参数变量)来使未绑定函数起作用,但是它与其他实例的预期签名不一致(如果我们是猴子,修补此实例):

>>> foo.sample_method(foo, 1, 2)
3

结论

现在,您知道可以执行此操作的几种方法,但是,认真地说-不要这样做。


#6楼

在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 fooFighters( self ):
...     print "fooFighters"
...
>>> A.fooFighters = fooFighters
>>> a2 = A()
>>> a2.fooFighters
<bound method A.fooFighters of <__main__.A instance at 0x00A9BEB8>>
>>> a2.fooFighters()
fooFighters

先前定义的实例也会被更新(只要它们本身没有覆盖属性):

>>> a.fooFighters()
fooFighters

当您要将方法附加到单个实例时,就会出现问题:

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

要绑定它,我们可以在类型模块中使用MethodType函数 :

>>> import types
>>> a.barFighters = types.MethodType( barFighters, a )
>>> a.barFighters
<bound method ?.barFighters of <__main__.A instance at 0x00A9BC88>>
>>> a.barFighters()
barFighters

这次,该类的其他实例未受影响:

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

通过阅读有关描述符和元类 编程的信息,可以找到更多信息。


#7楼

这实际上是“杰森·普拉特”答案的附加内容

尽管杰森斯(Jasons)回答有效,但只有在要向类中添加函数时才起作用。 当我尝试从.py源代码文件中重新加载现有方法时,它对我不起作用。

我花了很长时间才找到解决方法,但是这个技巧似乎很简单... 1.st从源代码文件中导入代码2.nd强制重新加载3.rd使用types.FunctionType(...)来转换导入并绑定到函数的方法,您还可以传递当前的全局变量,因为重新加载的方法将位于其他命名空间中。4.现在,您可以按照类型的“ Jason Pratt”的建议继续使用type.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 typesimport ReloadCodeDemo as ReloadMod # import the code as modulereload (ReloadMod) # force a reload of the modulemyM = getattr(ReloadMod.A,methodName) #get reloaded MethodmyTempFunc = types.FunctionType(# convert the method to a simple functionmyM.im_func.func_code, #the methods codeglobals(), # globals to useargdefs=myM.im_func.func_defaults # default values for variables if any) myNewM = types.MethodType(myTempFunc,self,self.__class__) #convert the function to a methodsetattr(self,methodName,myNewM) # add the method to the functionif __name__ == '__main__':a = A()a.bar()# now change your code and save the filea.reloadCode('bar') # reloads the filea.bar() # now executes the reloaded code

#8楼

这个问题是几年前提出的,但是,有一种简单的方法可以使用装饰器来模拟函数与类实例的绑定:

def binder (function, instance):copy_of_function = type (function) (function.func_code, {})copy_of_function.__bind_to__ = instancedef bound_function (*args, **kwargs):return copy_of_function (copy_of_function.__bind_to__, *args, **kwargs)return bound_functionclass SupaClass (object):def __init__ (self):self.supaAttribute = 42def new_method (self):print self.supaAttributesupaInstance = SupaClass ()
supaInstance.supMethod = binder (new_method, supaInstance)otherInstance = SupaClass ()
otherInstance.supaAttribute = 72
otherInstance.supMethod = binder (new_method, otherInstance)otherInstance.supMethod ()
supaInstance.supMethod ()

在那里,当您将函数和实例传递给活页夹装饰器时,它将创建一个新函数,其代码对象与第一个相同。 然后,该类的给定实例存储在新创建的函数的属性中。 装饰器返回一个(第三个)函数,该函数自动调用复制的函数,并将实例作为第一个参数。

总之,您将获得一个模拟它绑定到类实例的函数。 保留原始功能不变。


#9楼

我感到奇怪的是,没有人提到上面列出的所有方法都在添加的方法和实例之间创建了循环引用,从而导致对象在垃圾回收之前一直保持不变。 有一个古老的技巧通过扩展对象的类来添加描述符:

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

#10楼

from types import MethodTypedef method(self):print 'hi!'setattr( targetObj, method.__name__, MethodType(method, targetObj, type(method)) )

有了这个,你可以使用self指针


#11楼

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

#12楼

我认为以上答案错过了关键点。

让我们来一个带有方法的类:

class A(object):def m(self):pass

现在,让我们在ipython中玩它:

In [2]: A.m
Out[2]: <unbound method A.m>

好的,因此m()以某种方式成为A的未绑定方法。 但是真的是那样吗?

In [5]: A.__dict__['m']
Out[5]: <function m at 0xa66b8b4>

事实证明, m()只是一个函数,对它的引用已添加到A类字典中-没有魔术。 那为什么要给我们一个不受约束的方法? 这是因为该点未转换为简单的字典查找。 实际上是A .__ class __.__ getattribute __(A,'m')的调用:

In [11]: class MetaA(type):....:     def __getattribute__(self, attr_name):....:         print str(self), '-', attr_nameIn [12]: class A(object):....:     __metaclass__ = MetaAIn [23]: A.m
<class '__main__.A'> - m
<class '__main__.A'> - m

现在,我不确定为什么最后一行要打印两次,但是仍然很清楚那里发生了什么。

现在,默认的__getattribute__所做的是检查属性是否为所谓的描述符 ,即,是否实现了特殊的__get__方法。 如果实现该方法,则返回该__get__方法的结果。 回到我们的A类的第一个版本,这是我们拥有的:

In [28]: A.__dict__['m'].__get__(None, A)
Out[28]: <unbound method A.m>

而且由于Python函数实现了描述符协议,所以如果它们代表一个对象被调用,它们将通过__get__方法将自身绑定到该对象。

好的,如何为现有对象添加方法? 假设您不介意修补类,那么它很简单:

B.m = m

然后, 借助描述符魔术, Bm “成为”一个不受约束的方法。

而且,如果您只想向单个对象添加方法,则必须使用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'>

#13楼

通过查看不同绑定方法的结果,合并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 scopedef __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 withdef f0(self, x):print 'f0\tx = %d\tr = %d' % ( x, self.r)a = C() # created before modified instnace
b = C() # modified instnacedef f1(self, x): # bind internallyprint 'f1\tx = %d\tr = %d' % ( x, self.r )
def f2( self, x): # add to class instance's .__dict__ as method typeprint 'f2\tx = %d\tr = %d' % ( x, self.r )
def f3( self, x): # assign to class as method typeprint 'f3\tx = %d\tr = %d' % ( x, self.r )
def f4( self, x): # add to class instance's .__dict__ using a general functionprint '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 = 10k = 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 = 2c = 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>>

#14楼

自python 2.6起不推荐使用new模块,并在3.0版中将其删除,请使用类型

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

在下面的示例中,我故意从patch_me()函数中删除了返回值。 我认为提供返回值可能会使人相信patch返回一个新对象,这是不正确的-它修改了传入的对象。 可能这可以促进对猴子补丁的更严格的使用。

import typesclass A(object):#but seems to work for old style objects toopassdef patch_me(target):def method(target,x):print "x=",xprint "called from", targettarget.method = types.MethodType(method,target)#add more if neededa = 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'>

#15楼

由于此问题要求使用非Python版本,因此以下是JavaScript:

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

#16楼

在Python中,猴子修补通常通过覆盖您自己的类或函数签名来起作用。 以下是Zope Wiki的示例:

from SomeOtherProduct.SomeModule import SomeClass
def speak(self):return "ook ook eee eee eee!"
SomeClass.speak = speak

该代码将覆盖/创建一个在类上称为“讲话”的方法。 在杰夫·阿特伍德(Jeff Atwood) 最近关于猴子修补的文章中 。 他显示了C#3.0中的示例,这是我在工作中使用的当前语言。

向现有对象实例添加方法相关推荐

  1. python object的实例是什么_Python-向现有对象实例添加方法

    小编典典 在Python中,函数和绑定方法之间存在差异. >>> def foo(): ... print "foo" ... >>> clas ...

  2. Js对象如何添加方法、查看Api

    js万物皆对象,要带着观察对象的眼观去看待每一个函数.变量. 为什么要用到原型? Es6以前,js中没有如ooa编程当中的class,但是要用到类,怎么办呢,构造函数就应运而生,但是构造函数里面添加方 ...

  3. 创建控件时出错,未将对象引用设置到对象实例解决方法

    第一步,首先关闭aspx页面 第二步,在单击项目右击,选择"清理" 第三步,然后在打开aspx页面,就可以看到正常的页面了. 注:一次不行的话,多做几次. 如果还是不行的话,就看看 ...

  4. 安装vs 2015 x新建项目 显示(未将对象引用设置到对象实例) 处理方法

    转载于:https://www.cnblogs.com/xiaodaxiaonao/p/7353617.html

  5. 报错:未将对象引用设置到对象实例

    C# opc客户端访问opc服务器 ,OPC写入时错误 这些错误是我经历过的报错,记录一下 环境 PLC:S7-200 PC Access SMART DLL:注册并引用 OPCDAAuto.dll ...

  6. 【错误记录】Groovy 扩展方法调用报错 ( 静态扩展方法 或 实例扩展方法 需要分别配置 | 没有配置调用会报错 groovy.lang.MissingMethodException )

    文章目录 一.报错信息 二.解决方案 一.报错信息 定义 Thread 扩展方法 , 下面的扩展方法 class ThreadExt {public static Thread hello(Threa ...

  7. Python OOP:面向对象基础,定义类,创建对象/实例,self,创建多个对象,添加对象属性,访问对象属性,__init__方法,带参数的__init__,__str__方法,__del__方法

    一.理解面向对象 面向对象是⼀种抽象化的编程思想,很多编程语⾔中都有的⼀种思想. ⾯向对象就是将编程当成是⼀个事物,对外界来说,事物是直接使用的,不用去管他内部的情况.⽽编程就是设置事物能够做什么事. ...

  8. 动态添加方法 并且动态的执行 有类方法 对象方法

    2019独角兽企业重金招聘Python工程师标准>>> // // ViewController.m // TESTzz // // Created by point on 2017 ...

  9. JAVA中console方法怎么用_Java中Console对象实例代码

    Java中Console对象实例代码 发布于 2020-12-20| 复制链接 摘记: 在JDK 6中新增了java.io.Console类,可以让您取得字节为基础的主控台装置,例如,您可以藉由Sys ...

最新文章

  1. C语言结构体篇 结构体
  2. Java项目:新闻发布系统(java+Springboot+ssm+mysql+maven)
  3. 决策树算法之cart剪枝
  4. vue点击其它区域隐藏
  5. 安徽省计算机一级文化基础,计算机一级文化基础选择题
  6. BZOJ1103: [POI2007]大都市meg
  7. 如何使用Web.config的authentication节实现Form认证
  8. java中web错误返回码,Java-Web3j Transfer.sendFunds()返回错误“天然气...
  9. 前端好用的素材网站分享
  10. Mac操作指南:访问Windows共享文件
  11. 我用前世的五百次回眸换今生与你一次擦肩而过
  12. 富士通笔记本最新系统恢复方法——系统工具恢复
  13. android u盘怎么打开文件夹图标不显示不出来了,如何解决U盘图标不显示但资源管理器中还能看到U盘...
  14. 解决chrt: failed to set pid 0‘s policy: Operation not permitted
  15. 心理学家:人生最可怕的不是失去爱,而是失去这种能力
  16. 欧拉全新发布:基础软件的技术溢出效应或再现!
  17. praat 使用记录
  18. webpack打包 - webpack篇
  19. c++读取二进制文件
  20. 2D(横版)游戏开发心得

热门文章

  1. quadTree 论文Real-Time Generation of Continuous吃透了
  2. 【Java源码分析】集合框架-Collections工具类-Arrays工具类
  3. python中矩阵拼接_numpy实现合并多维矩阵、list的扩展方法
  4. Android之利用回调函数onCreateDialog实现加载对话框
  5. (017)java后台开发之客户端通过HTTP获取接口Json数据
  6. 测试私有方法 重构_通过测试学Go:指针和错误
  7. 软件测试2019:第七次作业—— 用户体验测试
  8. 如何阻止子元素触发父元素的事件
  9. 下载jdk文件后缀是.gz而不是.tar.gz怎么办
  10. [深入浅出Cocoa]iOS网络编程之Socket