Can't define __call__ within __init__?

N

Neal Becker

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
 
S

Steven D'Aprano

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.
 
N

Neal Becker

Steven said:
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.
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.
 
S

Steve Holden

Neal said:
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
 
A

Andre Engels

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.
 
M

MRAB

Andre said:
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.
Addendum: a bad algorithm is bad, whatever language it's written in.
 
S

Steve Howell

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

Peter Otten

Steve said:
On Mar 10, 7:18 pm, Steven D'Aprano
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:
.... def __init__(self):
.... self.__call__ = lambda: 42
........ def __init__(self):
.... self.__call__ = lambda: 42
....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
 
S

Steven D'Aprano

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.
 
S

Steven D'Aprano

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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top