method = Klass.othermethod considered PITA

Discussion in 'Python' started by John J. Lee, Jun 4, 2005.

  1. John J. Lee

    John J. Lee Guest

    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:

    >>> class A:

    .... def foo(s):print 'foo'
    .... bar = foo
    ....
    >>> a = A()
    >>> a.bar()

    foo
    >>> class B(A):

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

    foo
    >>>


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


    John
     
    John J. Lee, Jun 4, 2005
    #1
    1. Advertising

  2. John J. Lee

    Jeff Epler Guest

    On Sat, Jun 04, 2005 at 10:43:39PM +0000, John J. Lee wrote:
    > 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.

    >>> pickle.dumps(A)

    'c__main__\nA\np0\n.'
    >>> pickle.dumps(B)

    '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-----
     
    Jeff Epler, Jun 5, 2005
    #2
    1. Advertising

  3. John J. Lee wrote:
    > 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:
    >
    >
    >>>>class A:

    > ... def foo(s):print 'foo'
    > ... bar = foo
    > ...
    >>>>class B(A):

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

    > 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
     
    Steven Bethard, Jun 5, 2005
    #3
  4. John J. Lee wrote:
    > 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.
    >>> import pickle
    >>> class Foo:

    ... def foo(self):
    ... print "Hello, world!"
    ... bar = foo
    ...
    >>> pickle.dumps(Foo)

    'c__main__\nFoo\np0\n.'
    >>> pickle.dumps(Foo())

    '(i__main__\nFoo\np0\n(dp1\nb.'
     
    Leif K-Brooks, Jun 5, 2005
    #4
  5. Steven Bethard wrote:

    > John J. Lee wrote:
    >
    >> It seems nice to do this
    >>
    >> class Klass:
    >>
    >> def _makeLoudNoise(self, *blah):
    >> ...
    >>
    >> woof = _makeLoudNoise

    >
    > 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.

    --
    Erik Max Francis && && http://www.alcyone.com/max/
    San Jose, CA, USA && 37 20 N 121 53 W && AIM erikmaxfrancis
    Things are as they are because they were as they were.
    -- Thomas Gold
     
    Erik Max Francis, Jun 5, 2005
    #5
  6. Erik Max Francis wrote:
    > 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
     
    Steven Bethard, Jun 5, 2005
    #6
  7. John J. Lee

    Terry Reedy Guest

    > Steven Bethard wrote:
    >
    >> John J. Lee wrote:
    >>
    >>> It seems nice to do this
    >>>
    >>> class Klass:
    >>>
    >>> def _makeLoudNoise(self, *blah):
    >>> ...
    >>>
    >>> woof = _makeLoudNoise

    >>
    >> Out of curiosity, why do you want to do this?


    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
     
    Terry Reedy, Jun 5, 2005
    #7
  8. John J. Lee

    John J. Lee Guest

    Steven Bethard <> writes:

    > John J. Lee wrote:
    > > It seems nice to do this
    > > class Klass:
    > > def _makeLoudNoise(self, *blah):
    > > ...
    > > woof = _makeLoudNoise

    >
    > 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.


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

    > > ... def foo(s):print 'foo'
    > > ... bar = foo
    > > ...
    > >>>>class B(A):

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

    > > foo

    >
    > 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.


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


    I meant class instances.


    John
     
    John J. Lee, Jun 5, 2005
    #8
  9. John J. Lee

    John J. Lee Guest

    Jeff Epler <> writes:

    > On Sat, Jun 04, 2005 at 10:43:39PM +0000, John J. Lee wrote:
    > > 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.


    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.


    > > 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.
    >
    > >>> pickle.dumps(A)

    > 'c__main__\nA\np0\n.'
    > >>> pickle.dumps(B)

    > 'c__main__\nB\np0\n.'


    I meant class instances.


    John
     
    John J. Lee, Jun 5, 2005
    #9
  10. John J. Lee wrote:
    > Steven Bethard <> writes:
    >>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.


    Look closer. The second example pickles and unpickles an instance of
    class A.

    STeVe
     
    Steven Bethard, Jun 5, 2005
    #10
  11. Steven Bethard wrote:

    > 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.)

    --
    Erik Max Francis && && http://www.alcyone.com/max/
    San Jose, CA, USA && 37 20 N 121 53 W && AIM erikmaxfrancis
    To understand is to forgive, even oneself.
    -- Alexander Chase
     
    Erik Max Francis, Jun 5, 2005
    #11
  12. John J. Lee

    John J. Lee Guest

    Erik Max Francis <> writes:
    [...]
    > 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
     
    John J. Lee, Jun 8, 2005
    #12
  13. John J. Lee

    John J. Lee Guest

    Steven Bethard <> writes:
    > John J. Lee wrote:
    > > Steven Bethard <> writes:
    > >>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.

    >
    > 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
     
    John J. Lee, Jun 8, 2005
    #13
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Michael
    Replies:
    2
    Views:
    280
    Stephen Horne
    Apr 1, 2004
  2. Michael

    Re: [OT] Top posting is a PITA

    Michael, Apr 1, 2004, in forum: Python
    Replies:
    2
    Views:
    277
    Michael
    Apr 1, 2004
  3. Eric Mahurin

    idea: klass.from_s(str)

    Eric Mahurin, Aug 25, 2005, in forum: Ruby
    Replies:
    8
    Views:
    134
    Nikolai Weibull
    Aug 26, 2005
  4. Paganoni
    Replies:
    9
    Views:
    121
    Gregory Brown
    Apr 2, 2009
  5. Replies:
    0
    Views:
    182
Loading...

Share This Page