Adding a Method to an Existing Object Instance

后端 未结 16 2992
夕颜
夕颜 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 05:51

    I think that the above answers missed the key point.

    Let's have a class with a method:

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

    Now, let's play with it in ipython:

    In [2]: A.m
    Out[2]: 
    

    Ok, so m() somehow becomes an unbound method of A. But is it really like that?

    In [5]: A.__dict__['m']
    Out[5]: 
    

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

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

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

    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:

    B.m = m
    

    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:

    b.m = types.MethodType(m, b)
    

    By the way:

    In [2]: A.m
    Out[2]: 
    
    In [59]: type(A.m)
    Out[59]: 
    
    In [60]: type(b.m)
    Out[60]: 
    
    In [61]: types.MethodType
    Out[61]: 
    

提交回复
热议问题