Re: [Tutor] Overriding a method in a class

2011-05-15 Thread Terry Carroll

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

2011-05-14 Thread Peter Otten
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

2011-05-14 Thread Alan Gauld

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

2011-05-13 Thread Terry Carroll

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

2011-05-13 Thread David Knupp
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

2011-05-13 Thread Terry Carroll

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

2011-05-13 Thread Terry Carroll

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