473,486 Members | 1,640 Online
Bytes | Software Development & Data Engineering Community
Create Post

Home Posts Topics Members FAQ

Why doesn't __call__ lead to infinite recursion?

def foo(): pass

foo is a function
foo is a callable object
foo has method __call__ defined

foo.__call__ is a function
foo.__call__ is a callable object
foo.__call__ has method __call__ defined

foo.__call__.__call__ is a function...
This seems to go on forever. How does calling foo() not lead to
an infinite loop while trying to execute?
Jul 18 '05 #1
7 2479
In article <bh**********@absinth.dialog.net.pl>,
bromden <br*****@gazeta.pl.no.spam> wrote:
foo is a function
foo is a callable object
foo has method __call__ defined


true
true
false


Wrong. See my followup.
--
Aahz (aa**@pythoncraft.com) <*> http://www.pythoncraft.com/

This is Python. We don't care much about theory, except where it intersects
with useful practice. --Aahz
Jul 18 '05 #2
In article <2a*************************@posting.google.com> ,
Patrick Lioi <pa*****@novaroot.com> wrote:

def foo(): pass

foo is a function
foo is a callable object
foo has method __call__ defined

foo.__call__ is a function
foo.__call__ is a callable object
foo.__call__ has method __call__ defined


You're mixing up the distinction between objects and types. If you do

print foo.__dict__

you'll see that __call__ isn't there. However, if you try

print type(foo).__dict__

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.
--
Aahz (aa**@pythoncraft.com) <*> http://www.pythoncraft.com/

This is Python. We don't care much about theory, except where it intersects
with useful practice. --Aahz
Jul 18 '05 #3
aa**@pythoncraft.com (Aahz) writes:
[...]
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.


Still seems weird that type(foo).__call__.__call__ (etc.) is defined.
John
Jul 18 '05 #4
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
da***@dalkescientific.com
Jul 18 '05 #5
Aahz:
No time to investigate further, but all your examples used classic
classes instead of new-style classes; I'm pretty sure that new-style
classes will more closely emulate the way functions work. There's also
the wrinkle I didn't mention that functions use a dict proxy IIRC.


Interesting. Very interesting.
class XYZ(object): .... def __init__(self):
.... def abc(x):
.... print "Hello", x
.... self.__call__ = abc
.... def __call__(self, x):
.... print "Yo", x
.... xyz = XYZ()
xyz("fred") Yo fred
getattr(xyz, "__call__") <function abc at 0x0168CB70>

I wonder if this will affect any of my code.

It does explain the observed differences better, since FunctionType
in 2.3 is derived from object while my class was not.

Andrew
da***@dalkescientific.com
Jul 18 '05 #6
"Andrew Dalke" <ad****@mindspring.com> writes:
Aahz:
No time to investigate further, but all your examples used classic
classes instead of new-style classes; I'm pretty sure that new-style
classes will more closely emulate the way functions work. There's also
the wrinkle I didn't mention that functions use a dict proxy IIRC.


Interesting. Very interesting.


Yes :-)

You have to have something like this when you do things like 'print
type(foo)'. This should call the *types* *bound* __str__ method, not
try to call the *instances* *unbound* __str__ method...

Cheers,
mwh

--
Its unmanageable complexity has spawned more fear-preventing tools
than any other language, but the solution _should_ have been to
create and use a language that does not overload the whole goddamn
human brain with irrelevant details. -- Erik Naggum, comp.lang.lisp
Jul 18 '05 #7
In article <87************@pobox.com>, John J. Lee wrote:
aa**@pythoncraft.com (Aahz) writes:
[...]
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.


Still seems weird that type(foo).__call__.__call__ (etc.) is defined.

[I'm afraid I sent this 2 or 3 already in private by mistake; resending in
public. I apologize, it's late...]

The point is that down there, sits the C level which doesn't go
through the Python definition. When you call ``foo()``, ``type(foo)``
is asked at the *C* level how to call it (read: the type struct is
accessed and the call-behavior slot, if non-NULL, is called by plain
C function call - which can't be intercepted in C so there is no futher
recursion).

It so happens that classes, when asked this, go back into the Python
level and look for the `__call__` attribute. They do this for all
operations, giving you the dynamism and flexibility we all love.

OTOH, functions, when asked this, simply execute their code objects with the
given arguments. The attribute `__call__` on function is a "proxy" attribute.
It is not used by function objects for the call, it only exposes the C-level
call machinery at Python level. The same convention is used for other
built-in types and operations, so that from Python you see Python-level and
C-level methods in the same way. You only have to be aware of the distinction
when working with bizzare extension types that don't respect this convention,
or asking questions like this one about how it all works... (Another one: why
__getattribute__ is not infinitely recusive? Same explanation.)

--
Beni Cherniavsky <cb**@tx.technion.ac.il>

Look, Mom, no viruses! [Hint: I use GNU/Linux]
Jul 18 '05 #8

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

0
1604
by: Roz Lee | last post by:
I am trying to access a satellite assembly for a custom culture using Resource Manager. When I use the .getstring method I get the following error message: System.ArgumentException: I can't...
4
2609
by: LOPEZ GARCIA DE LOMANA, ADRIAN | last post by:
Hi all, I have a question with some code I'm writting: def main(): if option == 1: function_a()
0
1232
by: swati_mridu | last post by:
Hi all I am getting the following error on reading from a resource file (.resource). System.ArgumentException: The resource file is to be read for different languages for multilingual...
149
24958
by: Christopher Benson-Manica | last post by:
(Followups set to comp.std.c. Apologies if the crosspost is unwelcome.) strchr() is to strrchr() as strstr() is to strrstr(), but strrstr() isn't part of the standard. Why not? --...
4
1350
by: Gregory Piñero | last post by:
Hi, Would anyone be able to tell me why my function below is getting stuck in infinite recusion? Maybe I'm just tired and missing something obvious? def...
0
1027
by: GB | last post by:
All, There's a few messages around about the Environment.GetResourceString causing a "Resource lookup failed - infinite recursion detected." error but nothing detailed. I have a problem...
0
1302
by: GB | last post by:
All, I raised a problem a while ago about custom cultures in .NET and an error whereby a "Resource lookup failed - infinite recursion detected" exception was raised. Usually this was raised when...
0
2665
by: Nays | last post by:
Hi All I would be very grateful for any help with a problem I am having with Custom Cultures. I need to change the date format of a specific culture. I have created a custom culture that...
5
1927
by: Tinku | last post by:
I am sorry for asking this silly question, but i don't understand why it is happening please suggest me ================================= #include <stdio.h> int main() { static int i=1;...
0
7094
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
6964
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
7173
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
1
6839
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
7305
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
5427
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...
0
3066
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The...
1
598
muto222
php
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
0
259
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.