tags:

views:

82

answers:

3

How do I write a Python class that handles calls on undefined methods by first, getting the output of a function of the same name from a given module, and then, doing something further with that output?

For example, given add(x, y), doublerInstance.add(1, 1) should return 4.

I know _ _ getattr _ _() intercepts calls on undefined methods, and getattr() can retrieve a function object. But I don't know how get the arguments passed to the undefined call caught by _ _ getattr _ _() to the function retrieved by getattr().

EXAMPLE
Module functions.py:
def add(x, y):
    return x + 

Module doubler.py:
class Doubler:
  def __init__(self, source):
    self.source = source

  def __getattr__(self, attrname):
    fnc = getattr(self.source, attrname)
    return fnc() * 2

Session:
>import functions as f
>import doubler as d
>doublerInstance = d.Doubler(f)
>doublerInstance.add(1, 2)
<snip> TypeError: add() takes exactly 2 arguments, (0 given) 
END

I do understand the error -- getattr() returns a function to be run, and the call fnc() doesn't pass any arguments to that function -- here, add(). But how do I get the arguments passed in to the call dblr.add(1, 2) and pass those to the function returned by the getattr() call?

I'm looking for the right way to do this, not some usage of _ _ getattr _ _. I realize that decorator functions using @ might be a better tool here, but I don't yet understand those well enough to see whether they could be applied here.

ALSO -- what resource should I have looked at to figure this out for myself? I haven't found it in the Lutz books, the Cookbook, or the Python Library Reference.

+1  A: 

__getattr__ has to return the function - not the result from calling it:

class Doubler:
  def __init__(self, source):
    self.source = source

  def __getattr__(self, attrname):
    fnc = getattr(self.source, attrname)
    return lambda x,y : fnc(x,y) * 2

This uses a lambda expression; it returns a new function that doubles the output on fnc

Perhaps this test code will make it clearer:

import functions as f
import doubler as d
doublerInstance = d.Doubler(f)
print doublerInstance.add(1, 2)

doubleadd = doublerInstance.add

print doubleadd(1,2)
print doubleadd(2,3)
Douglas Leeder
+1  A: 

When you call doublerInstance.add(1, 2), you're getting an attribute add from it, and then you're calling it. But inside your getattr, you're returning a value. You have to return a function.

Anyway, for this particular case to work, you need this:

def __getattr__(self, attrname) :
    fnc = getattr(self.source, attrname)
    def doubled(*args, **kwargs) :
        return 2 * fnc(*args, **kwargs)
    return doubled
sykora
Thanks. All the answers are helpful, but this seems to admit the greatest variety of arguments and so seems the most general.I'd still like a resource that helped me _understand_ all this, and trace the movement of arguments from the intercepted method to the temporary function.
chernevik
A: 

Try this:

class Doubler:
def __init__(self, source):
    self.source = source

def __getattr__(self, attrname):
    def tmp_func(*args):
        fnc = getattr(self.source, attrname)
        return fnc(*args) * 2
    return tmp_func

Hint: getattr must return a function, not the result of the function call.