【问题标题】:Adding a Method to an Existing Object Instance向现有对象实例添加方法
【发布时间】:2014-12-22 12:23:13
【问题描述】:

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

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

【问题讨论】:

    标签: python oop methods monkeypatching


    【解决方案1】:

    如何从类的实例中恢复类

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

    【讨论】:

      【解决方案2】:

      除了别人说的,我发现__repr____str__方法不能在对象级别进行monkeypatched,因为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'
      

      【讨论】:

        【解决方案3】:

        在 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 function in the types module:

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

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

        【讨论】:

        • 与其手动创建MethodType,不如手动调用descriptor protocol 并让函数生成您的实例:barFighters.__get__(a) 为绑定到abarFighters 生成绑定方法。跨度>
        • @MartijnPieters 使用 descriptor protocol 与创建 MethodType 的任何优势,除了可能更具可读性。
        • @EndermanAPM:几个:它更有可能继续与访问实例上的属性完全相同的工作。它也适用于classmethodstaticmethod 以及其他描述符。它避免了另一个导入使命名空间混乱。
        • 建议的描述符方法的完整代码是a.barFighters = barFighters.__get__(a)
        • 请注意,python3 中没有未绑定方法,因为函数和未绑定方法之间的区别非常小,Python 3 消除了这种区别。
        【解决方案4】:

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

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

        此代码将覆盖/创建类中名为peak 的方法。在 Jeff Atwood 的 recent post on monkey patching 中,他展示了 C# 3.0 的示例,这是我当前用于工作的语言。

        【讨论】:

        • 但它会影响类的所有个实例,而不仅仅是一个。
        【解决方案5】:

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

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

        输出:

        This is instance string
        

        【讨论】:

          【解决方案6】:

          前言 - 关于兼容性的说明:其他答案可能仅适用于 Python 2 - 此答案在 Python 2 和 3 中应该可以很好地工作。如果只编写 Python 3,您可能会忽略显式继承自 object,否则代码应该保持不变。

          向现有对象实例添加方法

          我了解到可以在 Python 中将方法添加到现有对象(例如不在类定义中)。

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

          是的,有可能 - 但不推荐

          我不推荐这个。这是一个坏主意。不要这样做。

          这里有几个原因:

          • 您将为执行此操作的每个实例添加一个绑定对象。如果你经常这样做,你可能会浪费很多内存。绑定方法通常仅在其调用的短时间内创建,然后在自动垃圾收集时它们不再存在。如果您手动执行此操作,您将拥有一个引用绑定方法的名称绑定 - 这将阻止其在使用时进行垃圾收集。
          • 给定类型的对象实例通常在该类型的所有对象上都有自己的方法。如果您在其他地方添加方法,则某些实例将具有这些方法,而其他实例则没有。程序员不会预料到这一点,您可能会违反rule of least surprise
          • 既然还有其他非常好的理由不这样做,那么如果你这样做,你也会给自己带来不好的名声。

          因此,我建议您不要这样做,除非您有充分的理由。 最好在类定义中定义正确的方法less最好直接对类进行猴子补丁,如下所示:

          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)
          

          Method naught (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
          

          结论

          您现在知道了几种方法可以做到这一点,但说真的 - 不要这样做。

          【讨论】:

          • 免责声明是我想知道的。方法定义只是嵌套在类定义中的函数。
          • @Atcold 我已经在介绍中详细说明了避免这样做的原因。
          • __get__ 方法也需要类作为下一个参数:sample_method.__get__(foo, Foo)
          • @AidasBendoraitis 我不会说它“需要”它,它是应用描述符协议时提供的可选参数 - 但 python 函数不使用该参数:github.com/python/cpython/blob/master/Objects/funcobject.c#L581
          • 很好的解释和方法。
          【解决方案7】:
          from types import MethodType
          
          def method(self):
             print 'hi!'
          
          
          setattr( targetObj, method.__name__, MethodType(method, targetObj, type(method)) )
          

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

          【讨论】:

            【解决方案8】:

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

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

            【讨论】:

              【解决方案9】:

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

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

              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.m 给了我们一个未绑定的方法呢?这是因为点没有被翻译成简单的字典查找。这实际上是对 A.__class__.__getattribute__(A, 'm') 的调用:

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

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

              现在,默认的 __getattribute__ 所做的是检查属性是否为所谓的descriptor,即是否实现了特殊的 __get__ 方法。如果它实现了那个方法,那么返回的是调用那个 __get__ 方法的结果。回到我们的 A 类的第一个版本,这就是我们所拥有的:

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

              由于 Python 函数实现了描述符协议,如果它们被代表一个对象调用,它们会在它们的 __get__ 方法中将自己绑定到该对象。

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

              B.m = 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'>
              

              【讨论】:

                【解决方案10】:

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

                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 装饰器时,它将创建一个新函数,其代码对象与第一个函数相同。然后,类的给定实例存储在新创建的函数的属性中。装饰器返回一个(第三个)函数,自动调用复制的函数,将实例作为第一个参数。

                总之,你得到一个模拟它绑定到类实例的函数。保持原有功能不变。

                【讨论】:

                  【解决方案11】:

                  这实际上是对“Jason Pratt”答案的补充

                  虽然 Jasons 的答案有效,但它只有在想要向类中添加函数时才有效。 当我尝试从 .py 源代码文件重新加载已经存在的方法时,它对我不起作用。

                  我花了很长时间才找到解决方法,但诀窍似乎很简单...... 1.st从源代码文件中导入代码 2.nd强制重新加载 3.rd 使用types.FunctionType(...) 将导入绑定的方法转换为函数 您还可以传递当前的全局变量,因为重新加载的方法将位于不同的命名空间中 4.现在您可以按照“Jason Pratt”的建议继续 使用 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
                  

                  【讨论】:

                    【解决方案12】:

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

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

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

                    但它也处理更多有趣的用例,如 FAQ 中的 documentation 所示。

                    代码可在GitHub获取。

                    【讨论】:

                      【解决方案13】:

                      至少有两种方法可以在没有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>>
                      

                      有用的链接:
                      Data model - invoking descriptors
                      Descriptor HowTo Guide - invoking descriptors

                      【讨论】:

                        【解决方案14】:

                        模块 new 自 python 2.6 起已弃用并在 3.0 中删除,请使用 types

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

                        【讨论】:

                        • 如果添加的方法需要引用自身,这将如何工作?我的第一次尝试导致语法错误,但在方法的定义中添加 self 似乎不起作用。 import types class A(object):#but 似乎也适用于旧式对象 ax = 'ax' pass def patch_me(target): def method(target,x): print (self.ax) print ("x=" ,x) print ("call from", target) target.method = types.MethodType(method,target) #如果需要添加更多 a = A() print(a.ax)
                        【解决方案15】:

                        由于这个问题要求非 Python 版本,这里是 JavaScript:

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

                        【讨论】:

                          【解决方案16】:

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

                          【讨论】:

                          • addmethod改写如下def addmethod(self, method, name): self.__dict__[name] = types.MethodType( method, self )解决问题
                          【解决方案17】:

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

                          【讨论】:

                          • 你正在修补类Test,而不是它的一个实例。
                          • 您正在向类添加方法,而不是对象实例。
                          【解决方案18】:

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

                          【讨论】:

                          • 这是修补类A,而不是实例a
                          • 是否有理由使用setattr(A,'printme',printme) 而不是简单的A.printme = printme
                          • 在运行时构造方法名才有意义。
                          猜你喜欢
                          • 2010-12-25
                          • 1970-01-01
                          • 2020-04-01
                          相关资源
                          最近更新 更多