A __call__ "method" which is itself another callable class instance?

Skip Montanaro skip.montanaro at gmail.com
Fri May 8 08:24:23 EDT 2015


This is just a pedantic post. It has no real useful application, just
something to think about. I apologize that the setup is rather long. The
stuff to think about is at the end. (I also apologize for using rich
text/html. In this HTML world in which we live nowadays, I'm never certain
that mail systems preserve leading whitespace, even in plain text mail.)

I have a script which accepts a CSV file as input and the definition of a
transform function. It applies the transform function to each row of the
CSV file and produces a new CSV file on standard out.

One of the things I discovered I needed in certain circumstances was to
inject user-defined variables into the system. Since it's a single pass
sort of thing (not long-lived), I decided to inject those variables into
the function's globals. (I could have added them to builtins, I suppose,
but it didn't occur to me when I first wrote the script.)

The challenge was to find the appropriate globlas dictionary into which to
inject these variable definitions. My code wound up looking like this:

    if inspect.ismethod(func):
func_globals = func.im_func.func_globals
    elif inspect.isfunction(func):
func_globals = func.func_globals
    elif inspect.isbuiltin(func):
func_globals = sys.modules[func.__module__].__dict__
    elif inspect.ismethoddescriptor(func):
raise TypeError("Can't get globals of a method descriptor")

What if func is actually an instance of a class with a __call__ method? So
I added another branch:

    elif hasattr(func, "__call__"):
func = func.__call__

But now what? This required me to to start the process over again. I also
needed an else clause. Let's add that first:

    else:
raise TypeError("Don't know how to find globals from %s objects" %
type(func))

I need to repeat the tests in the case where I found a non-function-like
object with a __call__ attribute. I decided to wrap this logic in a for
loop which could only run twice:

    for _i in (0, 1):
        if inspect.ismethod(func):
            func_globals = func.im_func.func_globals
        elif inspect.isfunction(func):
            func_globals = func.func_globals
        elif inspect.isbuiltin(func):
            func_globals = sys.modules[func.__module__].__dict__
        elif inspect.ismethoddescriptor(func):
            raise TypeError("Can't get globals of a method descriptor")
        elif hasattr(func, "__call__"):
            func = func.__call__
            # Try again...
            continue
        else:
            raise TypeError("Don't know how to find globals from %s
objects" % type(func))
        break
    else:
        raise TypeError("Don't know how to find globals from %s objects" %
type(func))

I thought about using while True, but thought, "nah, that's never going to
happen." Then I wondered if it *could* happen. I constructed a short chain
of __call__ attributes which might require the loop to repeat more than
twice, but I can't think of a situation where it would actually be useful:

    class C1(object):
       def __call__(self):
           print "called"

    class C2(object):
       __call__ = C1()

If you instantiate C2 and call the instance, it works as you would expect:

    >>> o = C2()
    >>> o()
    called

>From that simple example, it's straightforward to create a chain of classes
of any length, thus requiring my for loop to potentially be a while True
loop. The question for the esteemed gathering here: have you ever
encountered the need for this class-instance-as-a-dunder-call-method
before? Perhaps in some sort of code generation situation? Or cleaner
functions-with-attributes?

Skip
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-list/attachments/20150508/1c6113dd/attachment.html>


More information about the Python-list mailing list