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

functors

2 views
Skip to first unread message

Tom Plunket

unread,
Jul 1, 2003, 9:25:18 PM7/1/03
to
How can I create a functor object in Python?

What I want (being a C++ coder <g>), is to be able to create an
object that I is callable. The following is my attempt, but it
doesn't work:

class Countdown:
def __init__(self):
self.callback = None

def SetCallback(self, time, callback):
self.callback = callback
self.timeRemaining = time

def Update(self):
if self.callback is not None:
self.timeRemaining -= 1
if self.timeRemaining <= 0:
print "Callback fired."
self.callback()
self.callback = None

class SomeClass:
def __init__(self):
self.countdown = Countdown()
self.countdown.SetCallback(30, lambda s=self: s.Callback)
# I have also tried 'lambda: self.Callback'
self.done = False

def Callback(self):
print "success!"
self.done = True

def Update(self):
while not self.done:
self.countdown.Update()

if __name__ == "__main__":
SomeClass().Update()

***

I wouldn't mind creating a new class to wrap this up (that's what
I'd do in C++, and that class would have an operator() defined),
but I can't seem to discover how to make callable objects in
Python.

If there's a better way to do callbacks like I'm trying to do,
also that would be handy to know. :) I don't want to poll the
countdown object every time through the loop because it just
seems "dirty"... :)

thanks,
-tom!

Troy Melhase

unread,
Jul 1, 2003, 9:46:28 PM7/1/03
to
Tom Plunket wrote:
> What I want (being a C++ coder <g>), is to be able to create an
> object that I is callable.

Hi Tom:

I'm not quite certain how you intend to use the classes you provided in your
original post, but the answer to your question is to define a __call__
method in your class. Untested example:

class SomeClass:
def __init__(self, factor):
self.factor = factor

def __call__(self, value):
return value * self.factor

my_object = SomeClass(3)
print my_object(4)

You can learn more about __call__ and other special functions for classes in
the Language Reference (section 3.3 for python2.2).

Good luck,
troy

David Eppstein

unread,
Jul 1, 2003, 9:42:27 PM7/1/03
to
In article <3jc4gvgn5rb4c4008...@4ax.com>,
Tom Plunket <to...@fancy.org> wrote:

> How can I create a functor object in Python?
>
> What I want (being a C++ coder <g>), is to be able to create an
> object that I is callable. The following is my attempt, but it
> doesn't work:

...


> class SomeClass:
> def __init__(self):
> self.countdown = Countdown()
> self.countdown.SetCallback(30, lambda s=self: s.Callback)

The error is in this line. Should be s.Callback().
As it is, your lambda is a function that returns a function object,
rather than calling it as you apparently intend.

> # I have also tried 'lambda: self.Callback'
> self.done = False

--
David Eppstein http://www.ics.uci.edu/~eppstein/
Univ. of California, Irvine, School of Information & Computer Science

Erik Max Francis

unread,
Jul 1, 2003, 10:22:12 PM7/1/03
to
Tom Plunket wrote:

> I wouldn't mind creating a new class to wrap this up (that's what
> I'd do in C++, and that class would have an operator() defined),
> but I can't seem to discover how to make callable objects in
> Python.

The __call__ method is the equivalent of C++'s operator ().

--
Erik Max Francis && m...@alcyone.com && http://www.alcyone.com/max/
__ San Jose, CA, USA && 37 20 N 121 53 W && &tSftDotIotE
/ \ Sometimes there's no point in giving up.
\__/ Louis Wu

Jack Diederich

unread,
Jul 2, 2003, 3:22:17 AM7/2/03
to
On Tue, Jul 01, 2003 at 06:25:18PM -0700, Tom Plunket wrote:
> How can I create a functor object in Python?
>
> What I want (being a C++ coder <g>), is to be able to create an
> object that I is callable. The following is my attempt, but it
> doesn't work:

I would make countdown dumber, and make the call more explicit

def countdown(thismany)
for (i) in range(thismany):
yield False
yield True
while True:
yield False

> def Update(self):
> while not self.done:
> self.countdown.Update()

becomes

def Update(self):
while not self.done:

if (self.countdown.next()):
self.Callback() # person reading the code knows what happens

but if you really want the hidden callback, just change the countdown()

def countdown(thismany, call_this):
for (i) in range(thismany):
yield 69 # ignored by caller anyway
call_this() # make the callback
while True:
yield 42 # more ignored yields

But I'm not sure if this is what you really want, the countdown() keeps going
even after it has fired. Hence the 'While True: yield False'

If you need the callback because you have a whole bunch and can't keep track
of each one individually, I'd make a parent class that decrements everyone
and pops people after they fire.

class Watcher(object):
def __init__(self):
self.watched = []
self.i = 0

def watch(count, call_this):
self.watched.append((self.i+count, call_this))

def decrement(self):
self.i += 1
callthese = filter(lambda x:x[0]<i, self.watched)
self.watched = filter(lambda x:x[0]>=i, self.watched)

for (dummy, callthis) in callthese:
callthis()

Just for fun, let's play with itertools

from itertools import *

def countdown(thismany):
return chain(repeat(False, thismany), [True], repeat(False))

or the abusive

def countdown(thismany, callthis):
def fakeout(): # because "lambda x:yield x()" isn't legal
yield callthis()
return chain(repeat(None, thismany-1), fakeout(), repeat(None))

and the totally abusive (and totally untested)

class Watcher(object):
def __init__(self):
self.watched = repeat(None)
self.curr = 0
def watch(self, count, callthis):
def fakeout():
yield callthis()
assert(count > self.curr) # new job must fire after the current longest one
self.watched = chain(repeat(None, count-1-self.curr), fakeout(), self.watched)
self.curr = count
def decrement(self):
if (self.curr):
self.curr -= 1
self.watched.next()

Enjoy,

-jackdied

Mirko Zeibig

unread,
Jul 2, 2003, 4:28:44 AM7/2/03
to
On Wed, 02 Jul 2003 at 01:25 GMT, Tom Plunket <to...@fancy.org> wrote:
Hello Tom,

it's even simpler and maybe to obvious for a C++-afficinado:

> class SomeClass:
> def __init__(self):
> self.countdown = Countdown()
> self.countdown.SetCallback(30, lambda s=self: s.Callback)

self.countdown.SetCallback(30, self.Callback)

You don't need lambda here, just give the Countdown class a reference to your
callback-method by specifying it's name *without* parentheses :-).

If you print self.Callback, you will the information, that this is a
instance-bound method object.

You can easily assign a new name to a function and call it afterwards:

li = []
al = li.append
al("Hello")
al("World")
print "".join(li)

This may even be faster as dot-resolution takes some time.

Regards
Mirko

Erik Max Francis

unread,
Jul 2, 2003, 4:43:37 AM7/2/03
to
Mirko Zeibig wrote:

> it's even simpler and maybe to obvious for a C++-afficinado:
>
> > class SomeClass:
> > def __init__(self):
> > self.countdown = Countdown()
> > self.countdown.SetCallback(30, lambda s=self: s.Callback)
>
> self.countdown.SetCallback(30, self.Callback)
>
> You don't need lambda here, just give the Countdown class a reference
> to your
> callback-method by specifying it's name *without* parentheses :-).
>
> If you print self.Callback, you will the information, that this is a
> instance-bound method object.

This feature of bound vs. unbound methods is one of the things that,
early on, made me really warm up to Python. The example you provide is
in fact the situation in which they're often the most useful: when
needing callbacks, usually for event-based systems (including GUIs).

C++ has member function pointers, but it doesn't have the standalone
concept of bound member functions, so functors are in fact usually used
for precisely this case, where you want to get a single callable object
that binds a member function pointer to an instance pointer/reference.
Python's builtin ability to usefully distinguish between bound and
unbound methods usually eliminates that need entirely; that is to say, a
bound method _is_ in fact a "functor," in that it's simply a callable
object that has the desired effect.

--
Erik Max Francis && m...@alcyone.com && http://www.alcyone.com/max/
__ San Jose, CA, USA && 37 20 N 121 53 W && &tSftDotIotE

/ \ I'm trying to forget / But I can't act as if we never met
\__/ Chante Moore

Bruno Desthuilliers

unread,
Jul 2, 2003, 7:37:36 AM7/2/03
to
Tom Plunket wrote:
> How can I create a functor object in Python?
>
> What I want (being a C++ coder <g>), is to be able to create an
> object that I is callable.

__call__ is your friend.

> The following is my attempt, but it
> doesn't work:
>
> class Countdown:
> def __init__(self):
> self.callback = None
>
> def SetCallback(self, time, callback):
> self.callback = callback
> self.timeRemaining = time
>
> def Update(self):
> if self.callback is not None:
> self.timeRemaining -= 1
> if self.timeRemaining <= 0:
> print "Callback fired."
> self.callback()
> self.callback = None
>
> class SomeClass:
> def __init__(self):
> self.countdown = Countdown()
> self.countdown.SetCallback(30, lambda s=self: s.Callback)

This is not the same problem as making an object (ie a class instance)
callable. Here you just want to use an instance object as callback function.

Functions in Python are first-class objects, so you don't need this
lambda stuff. This should work (I tried a simplified version...):

self.countdown.SetCallback(30, self.Callback)


HTH
Bruno

Tom Plunket

unread,
Jul 2, 2003, 11:34:36 PM7/2/03
to
Erik Max Francis wrote:

> The __call__ method is the equivalent of C++'s operator ().

Thanks guys.

-tom!

Tom Plunket

unread,
Jul 3, 2003, 1:55:03 AM7/3/03
to
Mirko Zeibig wrote:

> You don't need lambda here, just give the Countdown class a reference to your
> callback-method by specifying it's name *without* parentheses :-).

Heh oh yeah. The "instance-bound methods" I guess this is called
is what was really throwing me. :)

Thanks- this is the solution that I was looking for. Dunno why I
got stuck in the needing a lambda routine, but oh well. ;)

-tom!

Tom Plunket

unread,
Jul 4, 2003, 7:25:23 PM7/4/03
to
Jack Diederich wrote:

> I would make countdown dumber, and make the call more explicit
>
> def countdown(thismany)
> for (i) in range(thismany):
> yield False
> yield True
> while True:
> yield False

Hmm. What's the yield doing here? I can't find anything on it
in the PQR which has pretty much become my reference of choice
for Python. I can guess at its function from what I know about
other languages, but ... hmmm. Doesn't seem like what I want
really, and how would I restart it? I'm guessing that yield is
akin to return here but the next time the function is called,
program flow resumes at the yield, but... Yeah, I'm not really
sure how this solves my problem.

> But I'm not sure if this is what you really want, the countdown()
> keeps going even after it has fired. Hence the 'While True:
> yield False'

Right- maybe a similar solution (three return values) that would
allow me to pop the "finished" countdowns off of a stack or
something.

thanks for your help.

-tom!

0 new messages