Passing a function as an argument from within the same class?

Discussion in 'Python' started by zealalot, May 1, 2009.

  1. zealalot

    zealalot Guest

    So, I'm trying to come up with a way to pass a method (from the same
    class) as the default argument for another method in the same class.
    Unfortunately though, I keep getting "self not defined" errors since
    the class hasn't been read completely before it references itself.

    Is there a better way of doing this?

    --- CODE ---

    class SomeClass():
    def doNothing(self):
    pass
    def function1(self):
    print "Running function 1."
    def function2(self, passedFunction=self.doNothing):
    print "Running passed function."
    passedFunction()

    someObject = SomeClass()
    someObject.function2(someobject.function1)

    --- CODE ---

    Thanks,
    - Zealalot
    zealalot, May 1, 2009
    #1
    1. Advertising

  2. zealalot

    Guest

    Zealalot, probably there are some ways to do that, but a simple one is
    the following (not tested):

    def function2(self, passed_function=None):
    if passed_function is None:
    passed_function = self.doNothing
    ...

    Bye,
    bearophile
    , May 1, 2009
    #2
    1. Advertising

  3. zealalot

    CTO Guest

    Make doNothing a classmethod.

    class SomeClass:

    @classmethod
    def doNothing(cls):
    pass

    def function1(self):
    print "Running function 1"

    def function2(self, passedFunction=SomeClass.doNothing):
    print "Running passed function"
    passedFunction()

    someObject = SomeClass()
    someObject.function2()
    someObject.function2(someObject.function1)
    CTO, May 1, 2009
    #3
  4. zealalot

    zealalot Guest

    On May 1, 10:50 am, CTO <> wrote:
    > Make doNothing a classmethod.
    >
    > class SomeClass:
    >
    >     @classmethod
    >     def doNothing(cls):
    >         pass
    >
    >     def function1(self):
    >         print "Running function 1"
    >
    >     def function2(self, passedFunction=SomeClass.doNothing):
    >         print "Running passed function"
    >         passedFunction()
    >
    > someObject = SomeClass()
    > someObject.function2()
    > someObject.function2(someObject.function1)


    It's not surprising, but I've never heard of a classmethod before.
    Basically, I read that it basically removes the need for the 'self'
    argument. Very cool!

    And thanks for the quick response,

    - Zealalot
    zealalot, May 1, 2009
    #4
  5. zealalot

    Peter Otten Guest

    CTO wrote:

    > Make doNothing a classmethod.
    >
    > class SomeClass:
    >
    > @classmethod
    > def doNothing(cls):
    > pass
    >
    > def function1(self):
    > print "Running function 1"
    >
    > def function2(self, passedFunction=SomeClass.doNothing):
    > print "Running passed function"
    > passedFunction()
    >
    > someObject = SomeClass()
    > someObject.function2()
    > someObject.function2(someObject.function1)


    To make that run without error you have to jump through a lot of hoops:

    class SomeClass(object):
    @classmethod
    def doNothing(cls):
    pass
    def function1(self):
    print "Running function 1"

    def function2(self, passedFunction=SomeClass.doNothing):
    print "Running passed function"
    passedFunction()

    SomeClass.function2 = function2

    someObject = SomeClass()
    someObject.function2()
    someObject.function2(someObject.function1)

    And if you don't need access to the instance you may not need access to the
    class either. In this case you can simplify:

    def doNothing():
    pass

    class SomeClass(object):
    def function1(self):
    print "Running function 1"
    def function2(self, passedFunction=doNothing):
    print "Running passed function"
    passedFunction()

    If you do need information about the state of the instance you can either
    pass it explicitly

    class SomeClass(object):
    def doNothing(self):
    pass
    def function1(self):
    print "Running function 1"
    def function2(self, passedFunction=doNothing):
    print "Running passed function"
    passedFunction.__get__(self)()

    or (better, I think) use a sentinel as shown by Bearophile.

    Peter
    Peter Otten, May 1, 2009
    #5
  6. On Fri, 01 May 2009 08:11:01 -0700, zealalot wrote:

    > On May 1, 10:50 am, CTO <> wrote:
    >> Make doNothing a classmethod.
    >>
    >> class SomeClass:
    >>
    >>     @classmethod
    >>     def doNothing(cls):
    >>         pass
    >>
    >>     def function1(self):
    >>         print "Running function 1"
    >>
    >>     def function2(self, passedFunction=SomeClass.doNothing):
    >>         print "Running passed function"
    >>         passedFunction()
    >>
    >> someObject = SomeClass()
    >> someObject.function2()
    >> someObject.function2(someObject.function1)

    >
    > It's not surprising, but I've never heard of a classmethod before.
    > Basically, I read that it basically removes the need for the 'self'
    > argument. Very cool!


    Not so.

    When you call an ordinary method (an "instance method"), Python
    automatically inserts the object itself as the first argument. So you
    need to define the method with one extra parameter, self.

    (Using the name self is just a convention. You can call it anything you
    like.)

    For classmethods, Python automatically inserts not the object itself, but
    the object's *class* as the first argument. So you still need to define
    the method with an extra parameter, only now the convention is to call is
    cls rather than self.

    Here's an example:

    class Test(object):
    x = 0 # class attribute
    def __init__(self, x):
    self.x = x # instance attribute
    def spam(self):
    print self, self.x
    @classmethod
    def ham(cls):
    print cls, cls.x


    And in use:

    >>> t = Test(42)
    >>> t.spam()

    <__main__.Test object at 0xb7cccc6c> 42
    >>> t.ham()

    <class '__main__.Test'> 0


    There is one other related built-in decorator, staticmethod().
    staticmethod() tells Python not to pass any extra argument to the method.
    That means the inside a static method, you can't refer to either the
    instance (self) or the class! Needless to say, there aren't very many
    uses for staticmethod().



    --
    Steven
    Steven D'Aprano, May 1, 2009
    #6
  7. On Fri, 01 May 2009 07:35:40 -0700, zealalot wrote:

    > So, I'm trying to come up with a way to pass a method (from the same
    > class) as the default argument for another method in the same class.
    > Unfortunately though, I keep getting "self not defined" errors since the
    > class hasn't been read completely before it references itself.
    >
    > Is there a better way of doing this?


    My first instinct is to say "Don't do that!", but let's see if there's a
    way to get what you want. It's actually very easy: just put the
    definition of the passed method before the method you want to use it in,
    then refer to it by name *without* self.

    However, there is a catch: you need to manually pass in the instance,
    instead of letting Python do it for you.

    class Spam(object):
    def ham(self):
    return "ham"
    def spam(self, func=ham):
    return "spam is a tasty %s-like food product" % func(self)


    And in use:

    >>> obj = Spam()
    >>> obj.ham()

    'ham'
    >>> obj.spam()

    'spam is a tasty ham-like food product'



    --
    Steven
    Steven D'Aprano, May 1, 2009
    #7
  8. zealalot

    CTO Guest

    > Careful, bearophiles' answer remains the best one.
    >
    > The only reason your example worked is that you had already had
    > SomeClass defined (probably from a previous experiment).


    Scott is correct, and if bearophile and I ever give you conflicting
    advice, take bearophile's.

    A (corrected) bit of code that might serve your purpose would be
    as follows:

    class SomeClass:

    def doNothing():
    pass

    def function1(self):
    print "running function 1"

    def function2(self, passedFunction=doNothing):
    print "running passed function"
    passedFunction()

    again, though- bearophile's is the best, and most standard,
    answer here
    CTO, May 1, 2009
    #8
  9. zealalot

    Dave Angel Guest

    zealalot wrote:
    > On May 1, 10:50 am, CTO <> wrote:
    >
    >> Make doNothing a classmethod.
    >>
    >> class SomeClass:
    >>
    >> @classmethod
    >> def doNothing(cls):
    >> pass
    >>
    >> def function1(self):
    >> print "Running function 1"
    >>
    >> def function2(self, passedFunction=meClass.doNothing):
    >> print "Running passed function"
    >> passedFunction()
    >>
    >> someObject =omeClass()
    >> someObject.function2()
    >> someObject.function2(someObject.function1)
    >>

    >
    > It's not surprising, but I've never heard of a classmethod before.
    > Basically, I read that it basically removes the need for the 'self'
    > argument. Very cool!
    >
    > And thanks for the quick response,
    >
    > - Zealalot
    >
    >

    As you've probably figured out by now, that also gets an error, since
    you can't refer to the SomeClass until the definition is complete.
    Better is either the sentinel approach, or defining the function outside
    the class. Note that since the signature must match the passed
    function, you presumably need neither a self nor a cls. So keep it
    simple and define doNothing as a top-level function.

    But it's worth expanding on the notion of a classmethod. This type of
    function is callable with any object, or just with the class name
    itself. But it doesn't have an access to instance attributes. That's
    good, but not very common. Class attributes are more common, and
    they're attributes which are shared among all the objects of the class.
    Dave Angel, May 1, 2009
    #9
  10. zealalot

    Terry Reedy Guest

    zealalot wrote:
    > So, I'm trying to come up with a way to pass a method (from the same
    > class) as the default argument for another method in the same class.
    > Unfortunately though, I keep getting "self not defined" errors since
    > the class hasn't been read completely before it references itself.
    >
    > Is there a better way of doing this?
    >
    > --- CODE ---
    >
    > class SomeClass():
    > def doNothing(self):
    > pass
    > def function1(self):
    > print "Running function 1."
    > def function2(self, passedFunction=self.doNothing):
    > print "Running passed function."
    > passedFunction()
    >
    > someObject = SomeClass()
    > someObject.function2(someobject.function1)


    As Stephen D'Aprano indicated, this is very easy

    class SomeClass():
    def doNothing(self):
    print("Doing nothing")
    def function1(self):
    print ("Running function 1.")
    def function2(self, passedFunction=doNothing):
    print ("Running passed function.")
    passedFunction(self)

    someObject = SomeClass()
    someObject.function2()
    someObject.function2(SomeClass.function1)

    produces (with 3.0.1)

    Running passed function.
    Doing nothing
    Running passed function.
    Running function 1.

    Key 1: a class statement introduces a new local namespace. The body of
    the class statement is executed in that namespace. Default arguments
    are evaluated, when a def statement is executed, in the local namespace
    of that def statement. For methods, that is the local namespace of the
    class. Hence, 'passedFunction = doNothing' works fine.

    Key 2: When a parameter is a function, the signature of the default and
    passed args are effectively part of the required type for the args. In
    this case, passedFunction is a function with one parameter. When
    captured, doNothing is not yet a method of the yet-to-become Someclass.
    So non-defaults passed to .function2 do not have to be methods either.
    In Python 3, unbound methods are simply functions anyway.

    Terry Jan Reedy
    Terry Reedy, May 1, 2009
    #10
    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. Reckoner
    Replies:
    11
    Views:
    698
    Steven D'Aprano
    Jan 19, 2009
  2. AikidoGuy
    Replies:
    11
    Views:
    558
    Seebs
    Nov 21, 2011
  3. Trans
    Replies:
    3
    Views:
    193
    Robert Klemme
    Jan 26, 2005
  4. ±è ÁØ¿µ
    Replies:
    7
    Views:
    247
    Einar Magnús Boson
    Nov 27, 2008
  5. User1014
    Replies:
    1
    Views:
    183
    Richard Cornford
    Nov 30, 2006
Loading...

Share This Page