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

Can't define __call__ within __init__?

1 view
Skip to first unread message

Neal Becker

unread,
Mar 10, 2010, 8:12:14 AM3/10/10
to pytho...@python.org
Want to switch __call__ behavior. Why doesn't this work? What is the
correct way to write this?

class X (object):
def __init__(self, i):
if i == 0:
def __call__ (self):
return 0
else:
def __call_ (self):
return 1


x = X(0)

x()
TypeError: 'X' object is not callable


Simon Brunning

unread,
Mar 10, 2010, 8:27:31 AM3/10/10
to Neal Becker, python-list

__call__ is in the __init__ method's local namespace - you need to
bind it to the class's namespace instead:

X.__call__ = __call__

But this probably isn't what you want either, since all instances of X
will share the same method.

What are you trying to do? In your simple example, you'd be much
better off with a single __call__ method. But you knew that.

--
Cheers,
Simon B.

Mark Lawrence

unread,
Mar 10, 2010, 8:40:19 AM3/10/10
to pytho...@python.org
Neal Becker wrote:
> Want to switch __call__ behavior. Why doesn't this work? What is the
> correct way to write this?
>
> class X (object):
> def __init__(self, i):
> if i == 0:
> def __call__ (self):
> return 0
> else:
> def __call_ (self):
> return 1
>
>
> x = X(0)
>
> x()
> TypeError: 'X' object is not callable
>
>
I think it's because X has already been constructed when __init__ is
called, so __new__ is the place for your conditional code.

Neal Becker

unread,
Mar 10, 2010, 8:41:28 AM3/10/10
to pytho...@python.org
Simon Brunning wrote:

> On 10 March 2010 13:12, Neal Becker <ndbe...@gmail.com> wrote:
>> Want to switch __call__ behavior. Why doesn't this work? What is the
>> correct way to write this?
>>
>> class X (object):
>> def __init__(self, i):
>> if i == 0:
>> def __call__ (self):
>> return 0
>> else:
>> def __call_ (self):
>> return 1
>>
>>
>> x = X(0)
>>
>> x()
>> TypeError: 'X' object is not callable
>

> __call__ is in the __init__ method's local namespace - you need to
> bind it to the class's namespace instead:
>
> X.__call__ = __call__
>
> But this probably isn't what you want either, since all instances of X
> will share the same method.
>
> What are you trying to do? In your simple example, you'd be much
> better off with a single __call__ method. But you knew that.
>

Sorry, a bit early in the morning. This works:


class X (object):
def __init__(self, i):
if i == 0:

def F (self):
return 0
else:
def F (self):
return 1
self.F = F

def __call__ (self):
return self.F (self)

Not sure if there is a more elegant (or compact) way to write this.
Could __call__ be defined directly within __init__?

What I'm trying to do is make a callable whose behavior is switched based on
some criteria that will be fixed for all calls. In my example, this will
ultimately be determined by the setting of a command line switch.

Matt Nordhoff

unread,
Mar 10, 2010, 8:58:07 AM3/10/10
to pytho...@python.org

ISTM it would be prettiest to do:

class X(object):
def __init__(self, i):
self.flag = i == 0
def __call__(self):
if self.flag:
return 0
else:
return 1

Or, if the comparison isn't particularly expensive, it would look nicer
to just use self.i and do "self.i == 0" in __call__.

Not that it matters, but this is probably faster than your version, too,
since it saves a method call.

By the way, IIRC Python only looks up double-underscore methods on the
class, not the instance. That's why you had to indirect through self.F.
--
Matt Nordhoff

Duncan Booth

unread,
Mar 10, 2010, 12:39:27 PM3/10/10
to
Neal Becker <ndbe...@gmail.com> wrote:

> What I'm trying to do is make a callable whose behavior is switched
> based on some criteria that will be fixed for all calls. In my
> example, this will ultimately be determined by the setting of a
> command line switch.
>

If you want different behaviour its usually best to use different classes.

You can keep all the common behaviour in a base class and just override the
__call__ method for the different behaviour. Then use a factory function to
decide which class to instantiate or else override __new__ and make the
decision there. e.g.

>>> class X(object):
def __call__(self):
return 0
def __new__(cls, i):
if i!=0:
cls = Y
return object.__new__(cls)


>>> class Y(X):
def __call__(self):
return 1


>>> x = X(0)
>>> x()
0
>>> y = X(1)
>>> y()
1
>>> isinstance(x, X)
True
>>> isinstance(y, X)
True

P.S. I don't know what you did in your post but your Followup-To header is
pointing to a group on gmane which makes extra work for me replying. Please
don't do that.

Neal Becker

unread,
Mar 10, 2010, 1:23:02 PM3/10/10
to pytho...@python.org
Duncan Booth wrote:
...

>
> P.S. I don't know what you did in your post but your Followup-To header is
> pointing to a group on gmane which makes extra work for me replying.
> Please don't do that.

I'm sorry about that, there is some bad interaction between gmane's nntp-
smtp gateway and python's mail list. I don't know what to do about it. I
think the problem only happens on python's mail list (I've never seen it
reported on any of the MANY other lists I use via gmane).

Duncan Booth

unread,
Mar 10, 2010, 1:37:59 PM3/10/10
to
Neal Becker <ndbe...@gmail.com> wrote:

Are the other mailing lists gatewayed from Usenet? It may not matter if
there's a followup-to header on a mailing list, it probably just gets
ignored, but it does matter on Usenet (which after all is what Gmane is
emulating).

Neal Becker

unread,
Mar 10, 2010, 2:42:39 PM3/10/10
to pytho...@python.org
Duncan Booth wrote:

For the record, it isn't really gatewayed to usenet - it's just allowing you
to read your favorite ML via nntp - which is MUCH more sensible than
actually having all that mail delivered personally to you, if you read a lot
of lists.

Robert Kern

unread,
Mar 10, 2010, 3:06:34 PM3/10/10
to pytho...@python.org
On 2010-03-10 13:42 PM, Neal Becker wrote:
> For the record, it isn't really gatewayed to usenet - it's just allowing you
> to read your favorite ML via nntp - which is MUCH more sensible than
> actually having all that mail delivered personally to you, if you read a lot
> of lists.

python-list is also gatewayed to the real USENET group comp.lang.python in
addition to its GMane gateway. Duncan is reading comp.lang.python from a real
USENET server, not via python-list through his email client. Most of the other
lists you read via GMane aren't gatewayed to the real USENET, so your
Followup-To header never caused a problem for anyone else.

--
Robert Kern

"I have come to believe that the whole world is an enigma, a harmless enigma
that is made terrible by our own mad attempt to interpret it as though it had
an underlying truth."
-- Umberto Eco

Robert Kern

unread,
Mar 10, 2010, 3:08:38 PM3/10/10
to pytho...@python.org
On 2010-03-10 12:23 PM, Neal Becker wrote:
> Duncan Booth wrote:
> ...

>>
>> P.S. I don't know what you did in your post but your Followup-To header is
>> pointing to a group on gmane which makes extra work for me replying.
>> Please don't do that.
>
> I'm sorry about that, there is some bad interaction between gmane's nntp-
> smtp gateway and python's mail list. I don't know what to do about it. I
> think the problem only happens on python's mail list (I've never seen it
> reported on any of the MANY other lists I use via gmane).

I think it may be your news reader adding the Original-Followup-To header. I use
Thunderbird 3 to read this list via GMane, too, but my posts do not contain the
header. What newsreader are you using?

Neal Becker

unread,
Mar 10, 2010, 6:38:10 PM3/10/10
to pytho...@python.org
Robert Kern wrote:

> On 2010-03-10 12:23 PM, Neal Becker wrote:
>> Duncan Booth wrote:
>> ...
>>>

>>> P.S. I don't know what you did in your post but your Followup-To header
>>> is pointing to a group on gmane which makes extra work for me replying.
>>> Please don't do that.
>>

>> I'm sorry about that, there is some bad interaction between gmane's nntp-
>> smtp gateway and python's mail list. I don't know what to do about it.
>> I think the problem only happens on python's mail list (I've never seen
>> it reported on any of the MANY other lists I use via gmane).
>
> I think it may be your news reader adding the Original-Followup-To header.
> I use Thunderbird 3 to read this list via GMane, too, but my posts do not
> contain the header. What newsreader are you using?
>

knode.

Steven D'Aprano

unread,
Mar 10, 2010, 10:18:47 PM3/10/10
to
On Wed, 10 Mar 2010 08:12:14 -0500, Neal Becker wrote:

> Want to switch __call__ behavior. Why doesn't this work? What is the
> correct way to write this?
>
> class X (object):
> def __init__(self, i):
> if i == 0:
> def __call__ (self):
> return 0
> else:
> def __call_ (self):
> return 1


Others have already pointed out that there are two reasons that won't
work:

(1) you define __call__ as a local variable of the __init__ method which
then disappears as soon as the __init__ method completes; and

(2) special methods like __call__ are only called on the class, not the
instance, so you can't give each instance its own method.


Perhaps the best way to solve this is to use delegation:


def zero_returner():
return 0

def one_returner():
return 1


class X (object):
def __init__(self, i):
if i == 0:

self.func = zero_returner
else:
self.func = one_returner
def __call__(self, *args, **kwargs):
return self.func(*args, **kwargs)


zero_returner and one_returner can be any callable object, not
necessarily a function.

Of course, all this assumes that your solution isn't even simpler:

class X (object):
def __init__(self, i):

self.i = i
def __call__(self):
return self.i

but I assume if it was that simple, you would have done that already.

--
Steven

Neal Becker

unread,
Mar 11, 2010, 7:56:59 AM3/11/10
to pytho...@python.org
Steven D'Aprano wrote:

The example I showed was just a toy problem. The real problem is
I expect to call a function many times, and I want to avoid the overhead of
the 'if blah' everytime.

Steve Holden

unread,
Mar 11, 2010, 8:30:12 AM3/11/10
to pytho...@python.org
Neal Becker wrote:
> The example I showed was just a toy problem. The real problem is
> I expect to call a function many times, and I want to avoid the overhead of
> the 'if blah' everytime.
>
This is a premature optimization. First, make it work. Then (if it
doesn't work fast enough) make it work faster.

regards
Steve
--
Steve Holden +1 571 484 6266 +1 800 494 3119
See PyCon Talks from Atlanta 2010 http://pycon.blip.tv/
Holden Web LLC http://www.holdenweb.com/
UPCOMING EVENTS: http://holdenweb.eventbrite.com/

Andre Engels

unread,
Mar 11, 2010, 9:14:45 AM3/11/10
to Steve Holden, pytho...@python.org
On Thu, Mar 11, 2010 at 2:30 PM, Steve Holden <st...@holdenweb.com> wrote:

>> The example I showed was just a toy problem.  The real problem is
>> I expect to call a function many times, and I want to avoid the overhead of
>> the 'if blah' everytime.
>>
> This is a premature optimization. First, make it work. Then (if it
> doesn't work fast enough) make it work faster.

Corrolary: When you do make it faster, make it faster where it is slow.
Second corrolary: If making it fast is so important that these two
rules do not apply, Python is not your language of choice.


--
André Engels, andre...@gmail.com

MRAB

unread,
Mar 11, 2010, 11:01:41 AM3/11/10
to Python List
Addendum: a bad algorithm is bad, whatever language it's written in.

Steve Howell

unread,
Mar 11, 2010, 11:20:14 AM3/11/10
to
On Mar 10, 7:18 pm, Steven D'Aprano

<ste...@REMOVE.THIS.cybersource.com.au> wrote:
> On Wed, 10 Mar 2010 08:12:14 -0500, Neal Becker wrote:
> > Want to switch __call__ behavior.  Why doesn't this work?  What is the
> > correct way to write this?
>
> > class X (object):
> >     def __init__(self, i):
> >         if i == 0:
> >             def __call__ (self):
> >                 return 0
> >         else:
> >             def __call_ (self):
> >                 return 1
>
> Others have already pointed out that there are two reasons that won't
> work:
>
> (1) you define __call__ as a local variable of the __init__ method which
> then disappears as soon as the __init__ method completes; and
>
> (2) special methods like __call__ are only called on the class, not the
> instance, so you can't give each instance its own method.
>

Are you sure about that? This program prints 1, 2, 1, 2.

class Foo:
def __init__(self, a):
if a == 1:
self.__call__ = lambda: 1
else:
self.__call__ = lambda: 2

foo1 = Foo(1)
print foo1()

foo2 = Foo(2)
print foo2()

print foo1()
print foo2()

See here:

http://docs.python.org/reference/datamodel.html

Class instances
Class instances are described below. Class instances are callable
only when the class has a __call__() method; x(arguments) is a
shorthand for x.__call__(arguments).

Peter Otten

unread,
Mar 11, 2010, 11:36:03 AM3/11/10
to
Steve Howell wrote:

> On Mar 10, 7:18 pm, Steven D'Aprano
> <ste...@REMOVE.THIS.cybersource.com.au> wrote:

>> (2) special methods like __call__ are only called on the class, not the
>> instance, so you can't give each instance its own method.

> Are you sure about that? This program prints 1, 2, 1, 2.

You are using a classic class while the behaviour described above applies to
newstyle classes:

>>> class Foo:
... def __init__(self):
... self.__call__ = lambda: 42
...
>>> Foo()()
42
>>> class Bar(object):
... def __init__(self):
... self.__call__ = lambda: 42
...
>>> Bar()()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'Bar' object is not callable

I don't think it's a good idea to write new code that requires a classic
class.

Peter

Steven D'Aprano

unread,
Mar 11, 2010, 10:24:39 PM3/11/10
to
On Thu, 11 Mar 2010 08:20:14 -0800, Steve Howell wrote:

>> (2) special methods like __call__ are only called on the class, not the
>> instance, so you can't give each instance its own method.
>>
>>
> Are you sure about that? This program prints 1, 2, 1, 2.

The rules for classic classes are different. Since classic classes have
gone away in 3.0, and are becoming rarer in 2.x, I didn't bother to
mention the complication.

--
Steven

Steven D'Aprano

unread,
Mar 11, 2010, 10:30:02 PM3/11/10
to
On Thu, 11 Mar 2010 07:56:59 -0500, Neal Becker wrote:

> The example I showed was just a toy problem. The real problem is I
> expect to call a function many times, and I want to avoid the overhead
> of the 'if blah' everytime.

Unless the __call__ methods are very small, the overhead of one extra if
and one extra attribute lookup will be insignificant.


--
Steven

0 new messages