Re: [Tutor] Overriding a method in a class
On Sat, 14 May 2011, Alan Gauld wrote: Is there any reason you can'tt override in the uisual way by inheritance? Doh! Of course I should. I've written plenty of classes before, but never one intended to be inherited, and now that you point it out, it's obvious that that's what I should be doing here. Thanks. On Sat, 14 May 2011, Peter Otten wrote: If you define a function in the class body and then instantiate that class [snip] You can find a thorough explanation at http://users.rcn.com/python/download/Descriptor.htm Thanks, Peter; that's very helpful. I'll read that page in detail. ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Overriding a method in a class
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) ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Overriding a method in a class
Terry Carroll carr...@tjc.com wrote in message 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. Is there any reason you can'tt override in the uisual way by inheritance? It seems to me you are changing the behaviour of a class which means its now a different kind of thing. So it deserves to be a new class - a LowerThing or whatever. ### example, continued def addlower(self, s2): self.stuff.append(s2.lower()) # add it as lower case B = Thing() B.add_the_stuff=addlower But this doesn't add a method it adds a reference to a function. methods are not simply functions defined inside a class. methods need to be bound to the class for the self magic to happen. [There was a good post by Steven a few weeks ago that explained the difference. It might be worth searching out] HTH, Alan G. ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Overriding a method in a class
On Fri, 13 May 2011, Terry Carroll wrote: What I *expect* is to see ['abcdefg'] printed. What I get is: Sorry, mixed multiple examples; What I had expected was ['wxyz']. Still the same question though. BTW, I forgot to mention: Python 2.7.1, under Windows. (Still not used to the Python 2.x/3.x thing) ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Overriding a method in a class
I think the problem is this bit about overriding an object's internal method with one that is defined externally. My gut feeilng is that you'd now have to explicitly pass the object (i.e., self) as well as the string, i.e.: B.addstuff(B, WXYZ) ...which seems clunky. I'm not familiar with this particular technique. Is it common to do something like this? On Fri, 13 May 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? ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Overriding a method in a class
On Fri, 13 May 2011, David Knupp wrote: I think the problem is this bit about overriding an object's internal method with one that is defined externally. Yes, that describes the phenomenon. My gut feeilng is that you'd now have to explicitly pass the object (i.e., self) as well as the string, i.e.: B.addstuff(B, WXYZ) ...which seems clunky. But worse, it would mean that you couldn't have the default un-overridden method for those cases where you don't need to override it. I'm not familiar with this particular technique. Is it common to do something like this? Certainly the problem is common. For my specific case, I'm going to go with a Plan B of using callbacks; and provide default unbound callbacks present in the module, but not defined in the class itself. But I'd still like to have a better understanding of how the call gets transmuted from a two-argument call to a one-argument call based upon the target. I suspect something along these lines is going on: begin uninformed speculation When a bound method is called, Python inserts a reference to self as an additional first argument, before the other arguments. It does not do this with unbound methods. In my first case, when I created object A, the name add_the_stuff references the bound method add_the_stuff as defined in the Thing class. When add_stuff calls add_the_stuff, because add_the_stuff references a bound method, Python does the automagical insertion of the self argument. In my second case, when I created the object B, the name add_the_stuff *initially* references the bound method add_the_stuff; but then, when I override, I make B.add_the_stuff reference the unbound external method. Now, when addstuff calls add_the_stuff, Python sees that it is referencing an unbound method, and does not insert the self reference into the argument list. end uninformed speculation Now I'll wait for one of the experts to edify me. On Fri, 13 May 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? ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Overriding a method in a class
On Fri, 13 May 2011, Terry Carroll wrote: For my specific case, I'm going to go with a Plan B of using callbacks; and provide default unbound callbacks present in the module, but not defined in the class itself. Here's a callback-with-default approach, which works: ### Thing.py def add_the_stuff(obj, s1): obj.stuff.append(s1) class Thing(object): def __init__(self, callback=add_the_stuff): self.stuff = [] self.cb=callback def addstuff(self, text): self.cb(self, text) # # toy2.py ### import Thing A = Thing.Thing() A.addstuff(ABCDEFG) print A.stuff def addlower(obj, s2): obj.stuff.append(s2.lower()) B = Thing.Thing(callback=addlower) B.addstuff(WXYZ) print B.stuff Which produces, as expected: toy2.py ['ABCDEFG'] ['wxyz'] But still... But I'd still like to have a better understanding of how the call gets transmuted from a two-argument call to a one-argument call based upon the target. ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor