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
def function2(self, passed_function=None):
if passed_function is None:
passed_function = self.doNothing
...
Bye,
bearophile
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
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
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
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
> 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
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
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.
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
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