Decorator to inject function into __call__ of a class

Steven D'Aprano steve at REMOVE-THIS-cybersource.com.au
Sat Mar 13 14:48:28 EST 2010


On Sat, 13 Mar 2010 11:02:44 -0800, Jon Clements wrote:

> If I can re-explain slightly, say I have a class 'compute':
> 
> class Compute(object):
>     def __init__(self, something):
>         self.something = something
>     # misc other methods here.....
> 
> then...
> 
> class ComputeAdd(Compute):
>     pass


What's the purpose of the do-nothing subclass?


> If I do,
> 
> @inject
> def ComputeAdd(fst, snd):
>     return fst + snd
> 
> The end result should be a new class called ComputeAdd __init__'d with
> fst and snd,

That will cause a TypeError, because ComputeAdd inherits __init__ from 
Compute, which only takes a single argument. So you can't initialise it 
with two.


> which when called, returns fst + snd.
>
> Hope that makes sense.

None what so ever, even after reading the entire thread :-)

Forget about the *mechanism*, and focus on the *result* you want. Let me 
see if I can guess what result you want.

You want to start with a single base class, say Compute as defined above.

Then you want to write a function which interacts with Compute somehow, 
say:

def func(self, x, y):
    return self.something + x + y

and wave some sort of magic wand (a decorator? something else?) to get an 
object x so that:

x("a", "b")

returns self.something + "a" + "b".

Is that right? If not, I have no idea what you want! But if this is what 
you want, then this is probably the simplest approach:

Start by adding delegation to the Compute class:

class Compute(object):
    def __init__(self, something):
        self.something = something
    def __call__(self, *args, **kwargs):
        return self.function(*args, **kwargs)

Then you can initialise an instance of Compute, and add a function to it:

def func(self, x, y):
    return self.something + x + y

added = Computer("spam")
added.function = func

And then say:

added("ham", "eggs")
-> "spamhameggs"


You could possibly even turn this into a decorator:

# untested
def magic(baseclass, *args):
    instance = baseclass(*args)
    def decorator(func):
        instance.function = func
        return instance
    return decorator
        
@magic(Compute, "spam")
def func(self, x, y):
    return self.something + x + y


func("ham", "eggs")
-> "spamhameggs"




Hope this helps.


-- 
Steven



More information about the Python-list mailing list