Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

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

6 views
Skip to first unread message

zealalot

unread,
May 1, 2009, 10:35:40 AM5/1/09
to
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

bearoph...@lycos.com

unread,
May 1, 2009, 10:44:43 AM5/1/09
to
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

CTO

unread,
May 1, 2009, 10:50:06 AM5/1/09
to
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)


zealalot

unread,
May 1, 2009, 11:11:01 AM5/1/09
to

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

Peter Otten

unread,
May 1, 2009, 11:41:27 AM5/1/09
to
CTO wrote:

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

Scott David Daniels

unread,
May 1, 2009, 12:33:19 PM5/1/09
to
zealalot wrote:
> On May 1, 10:50 am, CTO <debat...@gmail.com> wrote:
>> Make doNothing a classmethod.
>>
>> class SomeClass:
>> @classmethod
>> def doNothing(cls):
>> ...

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

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

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).
At the time you are executing the "def" statements for the
methods of SomeClass, SomeClass is not yet defined. the default
arguments are evaluated at "def" time, but the method body is
evaluated at methd invokation time (well past the time SomeClass
is fully built.

Typically, classmethod is used to provide alternate constructors
for a class, though it does have other uses.

--Scott David Daniels
Scott....@Acm.Org

Steven D'Aprano

unread,
May 1, 2009, 1:00:25 PM5/1/09
to

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

unread,
May 1, 2009, 1:09:21 PM5/1/09
to
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

CTO

unread,
May 1, 2009, 1:26:12 PM5/1/09
to
> 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

Dave Angel

unread,
May 1, 2009, 2:47:30 PM5/1/09
to zealalot, pythonlist
zealalot wrote:
> On May 1, 10:50 am, CTO <debat...@gmail.com> 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.


Scott David Daniels

unread,
May 1, 2009, 3:13:58 PM5/1/09
to
Steven D'Aprano wrote:
> 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....

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

But you don't quite get the behavior the OP wanted, which was to allow
(text-producing in this case) methods as the argument:

>>> obj = Spam()


>>> obj.spam()
'spam is a tasty ham-like food product'

>>> obj.spam(obj.ham)
Traceback (most recent call last):
File "<pyshell#271>", line 1, in <module>
obj.spam(obj.ham)
File "<pyshell#268>", line 5, in spam


return "spam is a tasty %s-like food product" % func(self)

TypeError: ham() takes exactly 1 argument (2 given)

Now it is tough to use obj.

--Scott David Daniels
Scott....@Acm.Org

Terry Reedy

unread,
May 1, 2009, 5:37:48 PM5/1/09
to pytho...@python.org

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

0 new messages