Why doesn't __call__ lead to infinite recursion?

Andrew Dalke adalke at mindspring.com
Sat Aug 16 14:04:56 EDT 2003


Aahz:
> you'll see __call__ there.  When you do foo(), Python actually does
> type(foo).__call__(foo).  Because type(foo).__call__ is manipulating
> foo, you don't get the circular reference.

Not quite, but I don't understand how everything works so what I
say may also need corrections.

The call syntax "foo()" does two things.  The first is to
get the 'thing' used for the call and the second is to actually
call it.  The latter is not done recursively - if the returned
thing can't be called, the attempt at making the call fails.

If 'foo' is an instance, then the implementation code is
something like

   thing_to_call = getattr(foo, "__call__")
   if thing_to_call is not None:
      DO_CALL(thing_to_call, args, kwargs)

The 'DO_CALL' is not a Python function, it's part of
how the implementation works.

The getattr implementation for an instance first tries to
find "__call__" in foo's instance __dict__.  If that fails, it
looks in the parent class, and returns a bound method,
that is, a new 'thing' with references to the class method
and to the instance itself.  The DO_CALL does the
actual call to this thing with the new arguments.  The
bound method thing prepends the self parameter and
does the actual call to the underlying code.

Pretty complicated, and I don't think I was very clear
on that.  Here's an example though to show that

  foo()  != foo.__class__.__call__(foo)

>>> class SPAM:
...  def __init__(self):
...   def callme(x):
...    print "Hello", x
...   self.__call__ = callme
...  def __call__(self, x):
...   print "Hej", x
...
>>> spam = SPAM()
>>> spam("world")
Hello world
>>>
>>> spam.__class__.__call__(spam, "world")
Hej world
>>>

>>> getattr(spam, "__call__")
<function callme at 0x014DA9B0>
>>> getattr(spam, "__init__")
<bound method SPAM.__init__ of <__main__.SPAM instance at 0x013CF148>>
>>> getattr(SPAM, "__call__")
<unbound method SPAM.__call__>
>>>

I'm also missing something because I don't know how
functions work.  I thought it was always 'use
getattr(obj, "__call__") to get the thing to call then do
the call machinery on that thing", but it doesn't seem
to do that for functions.

>>> def f(x):
...  print "f(%s)" % x
...
>>> def g(x):
...  print "g(%s)" % x
...
>>> f(5)
f(5)
>>> f(7)
f(7)
>>> f.__call__ = g.__call__
>>> f(4)
f(4)
>>> f.__call__
<method-wrapper object at 0x014AD6F0>
>>> f.__call__
<method-wrapper object at 0x014AD6F0>
>>> g.__call__
<method-wrapper object at 0x014A02D0>
>>> g.__call__
<method-wrapper object at 0x014AD5B0>
>>>

I expected the 'f(4)' call to return 'g(4)' since I replaced
the function's __call__ with g's __call__.

What's throwing me off is that g.__call__ returns
a new wrapper object each time, while once I
assigned f.__call__, it persistently stayed that way.
So there's some getattr shenanigans with functions
I don't understand.  To make it worse, there's also

>>> import types
>>> types.FunctionType.__call__
<slot wrapper '__call__' of 'function' objects>
>>>

I'll leave the clarification to someone else.

                    Andrew
                    dalke at dalkescientific.com






More information about the Python-list mailing list