[Tutor] Overriding a method in a class

Peter Otten __peter__ at web.de
Sat May 14 08:20:31 CEST 2011


Terry Carroll wrote:

> I have a pretty basic point of confusion that I'm hoping I can have
> explained to me.  I have a class in which I want to override a method, and
> have my method defined externally to the class definition invoked instead.
> But when I do so, my external method is invoked with a different argument
> signature than the method it overrides.
> 
> (I'll illustrate with a toy example named toy.py that maintains a list of
> strings; the actual use case is a wxPython drag-and-drop shell that I find
> I keep re-using over and over, so I decided to try to turn it into a
> general-purpose module for my own use.)
> 
> ### example 1 begin
> 
> class Thing(object):
>      def __init__(self):
>          self.stuff = []
>      def addstuff(self, text):
>          self.add_the_stuff(text)
>      def add_the_stuff(self, s1):
>          self.stuff.append(s1)
> 
> A = Thing()
> A.addstuff("ABCDEFG")
> print A.stuff
> 
> ### example 1 end
> 
> So far, this works as expected.  addstuff invokes add_the_stuff; and the
> line "print A.stuff" prints out as ['ABCDEFG'], as expected.
> 
> Now, here's where I am getting befuddled, with the following additional
> lines:
> 
> ### example, continued
> def addlower(self, s2):
>      self.stuff.append(s2.lower()) # add it as lower case
> 
> B = Thing()
> B.add_the_stuff=addlower
> B.addstuff("WXYZ")
> print B.stuff
> ### end
> 
> My *intent* here is to patch the Thing object named B so that the
> B's add_the_stuff method is replaced with this additional addlower method
> that I define external to the object.  My expectation would be that, just
> as add_the_stuff method was called with two arguments (self and the
> string), the patched-in addlower would also get called the same way.
> 
> What I *expect* is to see ['abcdefg'] printed.  What I get is:
> 
> Traceback (most recent call last):
>    File "E:\Personal\py\DragDrop\toy.py", line 22, in <module>
>      B.addstuff("WXYZ")
>    File "E:\Personal\py\DragDrop\toy.py", line 7, in addstuff
>      self.add_the_stuff(text)
> TypeError: addlower() takes exactly 2 arguments (1 given)
> 
> I'm assuming I'm missing some fundamental concept.  What is it?

If you define a function in the class body and then instantiate that class

class A(object):
    def method(self, x): 
        print x

a = A()

an attribute access like

a.method

will be translated to

a.method.__get__(a, A)

which returns a bound method object. This is called "descriptor protocol", 
the same mechanism that allows the implementation of properties. You can 
find a thorough explanation at 
http://users.rcn.com/python/download/Descriptor.htm

However, if you put a function into an instance the attribute will be 
returned as is. You have to inform it about self either by explicitly 
invoking __get__()

def f(self, x): 
    print x*x

a.method = f.__get__(a, type(a))

or by using partial function application:

from functools import partial
a.method = partial(method, a)





More information about the Tutor mailing list