Adding a Method to an Existing Object Instance

后端 未结 16 2959
夕颜
夕颜 2020-11-21 05:45

I\'ve read that it is possible to add a method to an existing object (i.e., not in the class definition) in Python.

I understand that it\'s not always good to do so

相关标签:
16条回答
  • 2020-11-21 06:04
    from types import MethodType
    
    def method(self):
       print 'hi!'
    
    
    setattr( targetObj, method.__name__, MethodType(method, targetObj, type(method)) )
    

    With this, you can use the self pointer

    0 讨论(0)
  • 2020-11-21 06:09

    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.

    #!/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>>
    

    Personally, I prefer the external ADDMETHOD function route, as it allows me to dynamically assign new method names within an iterator as well.

    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>>
    
    0 讨论(0)
  • 2020-11-21 06:09

    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:

    def addmethod(obj, name, func):
        klass = obj.__class__
        subclass = type(klass.__name__, (klass,), {})
        setattr(subclass, name, func)
        obj.__class__ = subclass
    
    0 讨论(0)
  • 2020-11-21 06:11

    There are at least two ways for attach a method to an instance without 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>>
    

    Useful links:
    Data model - invoking descriptors
    Descriptor HowTo Guide - invoking descriptors

    0 讨论(0)
  • 2020-11-21 06:11

    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 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
    
    0 讨论(0)
  • 2020-11-21 06:12

    What Jason Pratt posted is correct.

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

    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.

    0 讨论(0)
提交回复
热议问题