method = Klass.othermethod considered PITA

J

John J. Lee

It seems nice to do this

class Klass:

def _makeLoudNoise(self, *blah):
...

woof = _makeLoudNoise


One probably wants the above to work as if you'd instead defined woof
in the more verbose form as follows:

def woof(self, *blah): return self._makeLoudNoise(self, *blah)

It doesn't, though. Two problems:

1. In derived classes, inheritance doesn't work right:
.... def foo(s):print 'foo'
.... bar = foo
........ def foo(s):print 'moo'
....
2. At least in 2.3 (and 2.4, AFAIK), you can't pickle classes that do
this.


John
 
J

Jeff Epler

1. In derived classes, inheritance doesn't work right:

Did you expect it to print 'moo'? I'd have been surprised, and expected
the behavior you got.
2. At least in 2.3 (and 2.4, AFAIK), you can't pickle classes that do
this.

In all the versions of Python I've used, classes are pickled by name.
This example you wrote doesn't pose any special problem when pickling.
'c__main__\nB\np0\n.'

Jeff

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.6 (GNU/Linux)

iD8DBQFCojrLJd01MZaTXX0RAgj0AJ0Yx1UCpuN01q28RmPq9o0fXFFxNACfR+ha
TijBt/NSdzxkyGiVMhA/7V0=
=FCih
-----END PGP SIGNATURE-----
 
S

Steven Bethard

John said:
It seems nice to do this

class Klass:

def _makeLoudNoise(self, *blah):
...

woof = _makeLoudNoise

Out of curiosity, why do you want to do this?
1. In derived classes, inheritance doesn't work right:


... def foo(s):print 'foo'
... bar = foo
...
... def foo(s):print 'moo'
...
foo

Depends on what you mean by "work right". It does do what you asked it
to do. You asked class A to store the "foo" object under the name
"bar". When you create an instance of B, and ask for the "bar"
attribute, it isn't found in class B, so Python looks to the parent
class. The parent class, A, does have an object named "bar", so Python
returns that. And that object is the same object that you asked be
named bar, namely the "foo" function.

If you want "bar" to be a function that *calls* the "foo" function,
declare it as such:

py> class A(object):
.... def foo(self):
.... print 'foo'
.... def bar(self):
.... return self.foo()
....
py> class B(A):
.... def foo(self):
.... print 'moo'
....
py> B().bar()
moo

2. At least in 2.3 (and 2.4, AFAIK), you can't pickle classes that do
this.

In Python 2.4:

py> class A(object):
.... def foo(self):
.... print 'foo'
.... bar = foo
....
py> import pickle
py> pickle.loads(pickle.dumps(A)).bar
<unbound method A.foo>
py> pickle.loads(pickle.dumps(A())).bar()
foo

Or maybe I misunderstand you?

STeVe
 
L

Leif K-Brooks

John said:
class Klass:

def _makeLoudNoise(self, *blah):
...

woof = _makeLoudNoise

[...]

At least in 2.3 (and 2.4, AFAIK), you can't pickle classes that do
this.

Works for me:

Python 2.3.5 (#2, May 4 2005, 08:51:39)
[GCC 3.3.5 (Debian 1:3.3.5-12)] on linux2
Type "help", "copyright", "credits" or "license" for more information. ... def foo(self):
... print "Hello, world!"
... bar = foo
... '(i__main__\nFoo\np0\n(dp1\nb.'
 
E

Erik Max Francis

Steven said:
Out of curiosity, why do you want to do this?

There aren't too many clear use cases, but I've found it useful from
time to time. Usually it comes in handy when you're specializing some
predefined behavior, often when relying on introspection in some fashion.

For instance, for a chat network bot framework, a certain form of bot
will look for any attribute in its instance that starts with verb_ and a
command and execute it when it hears it spoken:

def verb_hello(self, convo):
"Respond to a greeting."
convo.respond(random.choice(self.greetings))

If you'd like it to respond to more than one word like this, then you
only need to assign the additional verbs, rather than redefine them:

verb_hi = verb_hello
verb_yo = verb_hello
verb_wazzup = verb_hello

It actually does more than this, where a builtin help system (verb_help)
which relies on the docstrings of the verb_... methods will allow you to
get help on any defined verb automatically, and list the known verbs
when supplied without arguments. But this would list duplicate verbs
for the aliases (hi, yo, wazzup) above, which isn't optimal. So instead
I have another prefix, alias_, which acts as a verb, but won't be
automatically scanned as a verb when help is finding the list of valid
verbs:

alias_hi = verb_hello
...

That way, you get maximum effectiveness for minimum clutter.
 
S

Steven Bethard

Erik said:
For instance, for a chat network bot framework, a certain form of bot
will look for any attribute in its instance that starts with verb_ and a
command and execute it when it hears it spoken:

def verb_hello(self, convo):
"Respond to a greeting."
convo.respond(random.choice(self.greetings))

If you'd like it to respond to more than one word like this, then you
only need to assign the additional verbs, rather than redefine them:

verb_hi = verb_hello
verb_yo = verb_hello
verb_wazzup = verb_hello

Well if you want these to work with subclasses that change verb_hello to
do something else, one option is to write a simple decorator, and then
your lines above become something like:

verb_hi = caller(verb_hello)
verb_yo = caller(verb_hello)
verb_wazzup = caller(verb_hello)

or if you prefer to only create one such function:

_verb_hello_caller = caller(verb_hello)
verb_hi = _verb_hello_caller
verb_yo = _verb_hello_caller
verb_wazzup = _verb_hello_caller

Here's a simple example in action:

py> def caller(func):
.... def wrapper(self, *args, **kwargs):
.... return getattr(self, func.__name__)(*args, **kwargs)
.... return wrapper
....
py> class C(object):
.... def verb_hello(self):
.... print "C: hello"
.... verb_hi = caller(verb_hello)
....
py> class D(C):
.... def verb_hello(self):
.... print "D: hello"
....
py> D().verb_hi()
D: hello

Notice that verb_hi() still calls verb_hello() in the subclass.

STeVe
 
T

Terry Reedy

I have occasionally seen this usage where it made sense. I vaguely
remember cases where the same function/method met two demands that required
two different names. An example would be a __special__ method also exposed
publicly as 'special' ( something) without the underscores. Or some other
interface required a different name.

A related usage is a true specialization in which one or more parameters is
given a default or constant value.

Terry J. Reedy
 
J

John J. Lee

Steven Bethard said:
Out of curiosity, why do you want to do this?

I don't. It's just a habit I picked up from the standard library.

Depends on what you mean by "work right". It does do what you asked
it to do.

Well, gee, I guess so!

By "right" simply meant "according to the intent of the people who
tend to write such code" (and I do hope you're not going to get
over-literal about *that* non-clinically-precise statement). It's
obviously a tacit intent, though, hence the problem.

You asked class A to store the "foo" object under the name
"bar". When you create an instance of B, and ask for the "bar"
attribute, it isn't found in class B, so Python looks to the parent
class. The parent class, A, does have an object named "bar", so
Python returns that. And that object is the same object that you
asked be named bar, namely the "foo" function.

Yes. My point was simply that the simplicity of writing method2 =
method1 in a class body is an attractive nuisance.

If you want "bar" to be a function that *calls* the "foo" function,
declare it as such:

py> class A(object):
... def foo(self):
... print 'foo'
... def bar(self):
... return self.foo()
...
py> class B(A):
... def foo(self):
... print 'moo'
...
py> B().bar()
moo

It was my intent to push people to do that instead, yes.

In Python 2.4:

py> class A(object):
... def foo(self):
... print 'foo'
... bar = foo
...
py> import pickle
py> pickle.loads(pickle.dumps(A)).bar
<unbound method A.foo>
py> pickle.loads(pickle.dumps(A())).bar()
foo

I meant class instances.


John
 
J

John J. Lee

Jeff Epler said:
Did you expect it to print 'moo'? I'd have been surprised, and expected
the behavior you got.

Me too. It's at the time of *writing* the code, before the subclass
even exists, that the problem can arise, if you're not thinking about
derivation. Sure, if you're not thinking about derivation, you have
many other problems when somebody starts deriving from you, but, well,
here's another issue to remember.

Fine once you've noted this particular wrinkle, I suppose.

And, looking again at another use case (in urllib2, the http_error_30*
methods) I guess it's true that there are cases where methods are
really distinct but merely happen to have the same implementation, so
that method2 = method1 is actually what you want, hence better than a
trivial method definition.

In all the versions of Python I've used, classes are pickled by name.
This example you wrote doesn't pose any special problem when pickling.

'c__main__\nB\np0\n.'

I meant class instances.


John
 
E

Erik Max Francis

Steven said:
Well if you want these to work with subclasses that change verb_hello to
do something else, one option is to write a simple decorator, and then
your lines above become something like:

Note I was just giving a use case for the general construct, not
necessarily a use case for the pseudofeature the original poster was
requesting. In my particular case, there isn't much need to make sure
things are properly overridden in subclasses, since functionality tends
to get added, rather than modified. (The "Why would you want to do
that?" question was asked before he went on to show what wasn't working
for him.)
 
J

John J. Lee

Erik Max Francis said:
requesting. In my particular case, there isn't much need to make sure
things are properly overridden in subclasses, since functionality
tends to get added, rather than modified. (The "Why would you want to
[...]

Well done, have this gold star: *.


John
 
J

John J. Lee

Steven Bethard said:
Look closer. The second example pickles and unpickles an instance of
class A.

Oh, I guess it was functions as *instance* attributes that mess up
pickle.


John
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top