[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.python

Decorator to inject function into __call__ of a class

Jon Clements

3/13/2010 4:19:00 PM

This is semi-experimental and I'd appreciate opinions of whether it's
the correct design approach or not. It seems like a good idea, but it
doesn't mean it is.

I have a class 'A', this provides standard support functions and
exception handling.
I have 'B' and 'C' which specialise upon 'A'

What I'd like to achieve is something similar to:

@inject(B):
def some_function(a, b):
pass # something useful

The name 'some_function' is completely redundant -- don't need it,
don't actually care about the function afterwards, as long as it
becomes a __call__ of a 'B' *instance*.

I've basically got a huge list of functions, which need to be the
callable method of an object, and possibly at run-time, so I don't
want to do:

class Something(B):
def __call__(self, etc.. etc...):
pass # do something

I've got as far as type(somename, (B,), {}) -- do I then __init__ or
__new__ the object or...

In short, the function should be the __call__ method of an object that
is already __init__'d with the function arguments -- so that when the
object is called, I get the result of the the function (based on the
objects values).

Hope that makes sense,

Cheers,

Jon.






8 Answers

Patrick Maupin

3/13/2010 4:26:00 PM

0

On Mar 13, 10:19 am, Jon Clements <jon...@googlemail.com> wrote:

> What I'd like to achieve is something similar to:
>
> @inject(B):
>  def some_function(a, b):
>      pass # something useful

So, just typing at the keyboard here, you mean something like:

class InjectClass(object):
def __init__(self, func, *args, **kw):
self.func = func
self.args = args
self.kw = kw
def __call__(self):
self.func(*self.args, **self.kw)

Or exactly what are you looking for?

Pat

Jon Clements

3/13/2010 4:38:00 PM

0

On 13 Mar, 16:26, Patrick Maupin <pmau...@gmail.com> wrote:
> On Mar 13, 10:19 am, Jon Clements <jon...@googlemail.com> wrote:
>
> > What I'd like to achieve is something similar to:
>
> > @inject(B):
> >  def some_function(a, b):
> >      pass # something useful
>
> So, just typing at the keyboard here, you mean something like:
>
> class InjectClass(object):
>     def __init__(self, func, *args, **kw):
>         self.func = func
>         self.args = args
>         self.kw = kw
>     def __call__(self):
>         self.func(*self.args, **self.kw)
>
> Or exactly what are you looking for?
>
> Pat

Not quite.

Let's say I have function 'F':

def add(a, b): return a + b

And a base class of 'C' which does all the __init__ stuff or
whatever's needed,
the function 'add' should return a new class __init__'d with a and b,
but 'add'
should be the __call__ of that instance.

Hope that makes sense, and TY for your post,

Jon.

Jack Diederich

3/13/2010 4:43:00 PM

0

On Sat, Mar 13, 2010 at 11:19 AM, Jon Clements <joncle@googlemail.com> wrote:
> This is semi-experimental and I'd appreciate opinions of whether it's
> the correct design approach or not. It seems like a good idea, but it
> doesn't mean it is.
>
> I have a class 'A', this provides standard support functions and
> exception handling.
> I have 'B' and 'C' which specialise upon 'A'
>
> What I'd like to achieve is something similar to:
>
> @inject(B):
>  def some_function(a, b):
>     pass # something useful
>
> The name 'some_function' is completely redundant -- don't need it,
> don't actually care about the function afterwards, as long as it
> becomes a __call__ of a 'B' *instance*.
>
> I've basically got a huge list of functions, which need to be the
> callable method of an object, and possibly at run-time, so I don't
> want to do:
>
> class Something(B):
>    def __call__(self, etc.. etc...):
>         pass # do something
>
> I've got as far as type(somename, (B,), {}) -- do I then __init__ or
> __new__ the object or...
>
> In short, the function should be the __call__ method of an object that
> is already __init__'d with the function arguments -- so that when the
> object is called, I get the result of the the function (based on the
> objects values).

I'm not sure exactly what you are asking for, but if what you want is
a bunch of different objects that vary only by their class's __call__
you could do it with a function that returns a new class based on A
but with a new __call__:

def make_new_call_class(base_class, call_func):
class NewClass(base_class):
def __call__(self, *args, **kw):
return call_func(self, *args, *kw)
return NewClass

or the return could even be NewClass() [return an instance] if this is
a one off.

That said, I'm not really sure what this behavior is good for.

-Jack

Patrick Maupin

3/13/2010 5:03:00 PM

0

On Mar 13, 10:38 am, Jon Clements <jon...@googlemail.com> wrote:
> On 13 Mar, 16:26, Patrick Maupin <pmau...@gmail.com> wrote:
>
>
>
> > On Mar 13, 10:19 am, Jon Clements <jon...@googlemail.com> wrote:
>
> > > What I'd like to achieve is something similar to:
>
> > > @inject(B):
> > >  def some_function(a, b):
> > >      pass # something useful
>
> > So, just typing at the keyboard here, you mean something like:
>
> > class InjectClass(object):
> >     def __init__(self, func, *args, **kw):
> >         self.func = func
> >         self.args = args
> >         self.kw = kw
> >     def __call__(self):
> >         self.func(*self.args, **self.kw)
>
> > Or exactly what are you looking for?
>
> > Pat
>
> Not quite.
>
> Let's say I have function 'F':
>
> def add(a, b): return a + b
>
> And a base class of 'C' which does all the __init__ stuff or
> whatever's needed,
> the function 'add' should return a new class __init__'d with a and b,
> but 'add'
> should be the __call__ of that instance.
>
> Hope that makes sense, and TY for your post,
>
> Jon.

Well, you could do it with a class. But if I'm understanding
correctly, maybe it's simpler than that:

>>> def inject(*args, **kw):
.... def wrapper(func):
.... def go():
.... return func(*args, **kw)
.... return go
.... return wrapper
....
>>> @inject(20, 22)
.... def add(a, b):
.... return a + b
....
>>> add()
42

Jon Clements

3/13/2010 5:10:00 PM

0

On 13 Mar, 16:42, Jack Diederich <jackd...@gmail.com> wrote:
> On Sat, Mar 13, 2010 at 11:19 AM, Jon Clements <jon...@googlemail.com> wrote:
> > This is semi-experimental and I'd appreciate opinions of whether it's
> > the correct design approach or not. It seems like a good idea, but it
> > doesn't mean it is.
>
> > I have a class 'A', this provides standard support functions and
> > exception handling.
> > I have 'B' and 'C' which specialise upon 'A'
>
> > What I'd like to achieve is something similar to:
>
> > @inject(B):
> >  def some_function(a, b):
> >     pass # something useful
>
> > The name 'some_function' is completely redundant -- don't need it,
> > don't actually care about the function afterwards, as long as it
> > becomes a __call__ of a 'B' *instance*.
>
> > I've basically got a huge list of functions, which need to be the
> > callable method of an object, and possibly at run-time, so I don't
> > want to do:
>
> > class Something(B):
> >    def __call__(self, etc.. etc...):
> >         pass # do something
>
> > I've got as far as type(somename, (B,), {}) -- do I then __init__ or
> > __new__ the object or...
>
> > In short, the function should be the __call__ method of an object that
> > is already __init__'d with the function arguments -- so that when the
> > object is called, I get the result of the the function (based on the
> > objects values).
>
> I'm not sure exactly what you are asking for, but if what you want is
> a bunch of different objects that vary only by their class's __call__
> you could do it with a function that returns a new class based on A
> but with a new __call__:
>
> def make_new_call_class(base_class, call_func):
>   class NewClass(base_class):
>     def __call__(self, *args, **kw):
>       return call_func(self, *args, *kw)
>   return NewClass
>
> or the return could even be NewClass() [return an instance] if this is
> a one off.
>
> That said, I'm not really sure what this behavior is good for.
>
> -Jack

Cheers Jack for the response.

The behaviour is to not derive from a class, but rather allow
the decorators to do so... so I would like to iterate over
a list of functions (don't care what they're called) and then
inject the function as a method. If needs be at run-time.

Say I have 1000 functions (okay, admittedly over quoted), but
I don't want every programmer to inherit from 'B' or 'C', but
to 'inject'. So the idea is that classes are pre-defined, have
predictable behaviour, *except* the __call__ is different.

You are correct in this. Why do I want that behaviour? ->

- It's easier, no inheriting from a class, when needs not.
- Some integrity (anyone can define a function and 'inject' to the
Management class)
- Easier maintainability - maybe :)

for i in function_list:
i = inject(function_list)

At the end of the day:
def blah(x, y, z):
pass

That should be the callable of the object.

Cheers again,

Jon.

Jack Diederich

3/13/2010 5:42:00 PM

0

On Sat, Mar 13, 2010 at 12:10 PM, Jon Clements <joncle@googlemail.com> wrote:
> On 13 Mar, 16:42, Jack Diederich <jackd...@gmail.com> wrote:
>> On Sat, Mar 13, 2010 at 11:19 AM, Jon Clements <jon...@googlemail.com> wrote:
>> > This is semi-experimental and I'd appreciate opinions of whether it's
>> > the correct design approach or not. It seems like a good idea, but it
>> > doesn't mean it is.
>>
>> > I have a class 'A', this provides standard support functions and
>> > exception handling.
>> > I have 'B' and 'C' which specialise upon 'A'
>>
>> > What I'd like to achieve is something similar to:
>>
>> > @inject(B):
>> >  def some_function(a, b):
>> >     pass # something useful
>>
>> > The name 'some_function' is completely redundant -- don't need it,
>> > don't actually care about the function afterwards, as long as it
>> > becomes a __call__ of a 'B' *instance*.
>>
>> > I've basically got a huge list of functions, which need to be the
>> > callable method of an object, and possibly at run-time, so I don't
>> > want to do:
>>
>> > class Something(B):
>> >    def __call__(self, etc.. etc...):
>> >         pass # do something
>>
>> > I've got as far as type(somename, (B,), {}) -- do I then __init__ or
>> > __new__ the object or...
>>
>> > In short, the function should be the __call__ method of an object that
>> > is already __init__'d with the function arguments -- so that when the
>> > object is called, I get the result of the the function (based on the
>> > objects values).
>>
>> I'm not sure exactly what you are asking for, but if what you want is
>> a bunch of different objects that vary only by their class's __call__
>> you could do it with a function that returns a new class based on A
>> but with a new __call__:
>>
>> def make_new_call_class(base_class, call_func):
>>   class NewClass(base_class):
>>     def __call__(self, *args, **kw):
>>       return call_func(self, *args, *kw)
>>   return NewClass
>>
>> or the return could even be NewClass() [return an instance] if this is
>> a one off.
>>
>> That said, I'm not really sure what this behavior is good for.
>>
>> -Jack
>
> Cheers Jack for the response.
>
> The behaviour is to not derive from a class, but rather allow
> the decorators to do so... so I would like to iterate over
> a list of functions (don't care what they're called) and then
> inject the function as a method. If needs be at run-time.
>
> Say I have 1000 functions (okay, admittedly over quoted), but
> I don't want every programmer to inherit from 'B' or 'C', but
> to 'inject'. So the idea is that classes are pre-defined, have
> predictable behaviour, *except* the __call__ is different.
>
> You are correct in this. Why do I want that behaviour? ->
>
> - It's easier, no inheriting from a class, when needs not.
> - Some integrity (anyone can define a function and 'inject' to the
> Management class)
> - Easier maintainability - maybe :)
>
> for i in function_list:
>    i = inject(function_list)
>
> At the end of the day:
> def blah(x, y, z):
>   pass
>
> That should be the callable of the object.

I'm still not sure why you are trying to do this, but you can do it
with delegation. Have the parent class's __call__ look for an
instance attribute named call_this and then call it, ex/

class A():
def __call__(self, *args, **kw):
self.call_this(*args, **kw) # we grab this off the instance

ob = A()
def my_func(*stuff): pass
ob.call_this = my_func

-Jack

Jon Clements

3/13/2010 7:03:00 PM

0

On 13 Mar, 17:42, Jack Diederich <jackd...@gmail.com> wrote:
> On Sat, Mar 13, 2010 at 12:10 PM, Jon Clements <jon...@googlemail.com> wrote:
> > On 13 Mar, 16:42, Jack Diederich <jackd...@gmail.com> wrote:
> >> On Sat, Mar 13, 2010 at 11:19 AM, Jon Clements <jon...@googlemail.com> wrote:
> >> > This is semi-experimental and I'd appreciate opinions of whether it's
> >> > the correct design approach or not. It seems like a good idea, but it
> >> > doesn't mean it is.
>
> >> > I have a class 'A', this provides standard support functions and
> >> > exception handling.
> >> > I have 'B' and 'C' which specialise upon 'A'
>
> >> > What I'd like to achieve is something similar to:
>
> >> > @inject(B):
> >> >  def some_function(a, b):
> >> >     pass # something useful
>
> >> > The name 'some_function' is completely redundant -- don't need it,
> >> > don't actually care about the function afterwards, as long as it
> >> > becomes a __call__ of a 'B' *instance*.
>
> >> > I've basically got a huge list of functions, which need to be the
> >> > callable method of an object, and possibly at run-time, so I don't
> >> > want to do:
>
> >> > class Something(B):
> >> >    def __call__(self, etc.. etc...):
> >> >         pass # do something
>
> >> > I've got as far as type(somename, (B,), {}) -- do I then __init__ or
> >> > __new__ the object or...
>
> >> > In short, the function should be the __call__ method of an object that
> >> > is already __init__'d with the function arguments -- so that when the
> >> > object is called, I get the result of the the function (based on the
> >> > objects values).
>
> >> I'm not sure exactly what you are asking for, but if what you want is
> >> a bunch of different objects that vary only by their class's __call__
> >> you could do it with a function that returns a new class based on A
> >> but with a new __call__:
>
> >> def make_new_call_class(base_class, call_func):
> >>   class NewClass(base_class):
> >>     def __call__(self, *args, **kw):
> >>       return call_func(self, *args, *kw)
> >>   return NewClass
>
> >> or the return could even be NewClass() [return an instance] if this is
> >> a one off.
>
> >> That said, I'm not really sure what this behavior is good for.
>
> >> -Jack
>
> > Cheers Jack for the response.
>
> > The behaviour is to not derive from a class, but rather allow
> > the decorators to do so... so I would like to iterate over
> > a list of functions (don't care what they're called) and then
> > inject the function as a method. If needs be at run-time.
>
> > Say I have 1000 functions (okay, admittedly over quoted), but
> > I don't want every programmer to inherit from 'B' or 'C', but
> > to 'inject'. So the idea is that classes are pre-defined, have
> > predictable behaviour, *except* the __call__ is different.
>
> > You are correct in this. Why do I want that behaviour? ->
>
> > - It's easier, no inheriting from a class, when needs not.
> > - Some integrity (anyone can define a function and 'inject' to the
> > Management class)
> > - Easier maintainability - maybe :)
>
> > for i in function_list:
> >    i = inject(function_list)
>
> > At the end of the day:
> > def blah(x, y, z):
> >   pass
>
> > That should be the callable of the object.
>
> I'm still not sure why you are trying to do this, but you can do it
> with delegation.  Have the parent class's __call__ look for an
> instance attribute named call_this and then call it, ex/
>
> class A():
>   def __call__(self, *args, **kw):
>     self.call_this(*args, **kw)  # we grab this off the instance
>
> ob = A()
> def my_func(*stuff): pass
> ob.call_this = my_func
>
> -Jack

Jack, thanks very much for your replies -- hugely appreciated.

I was delayed by the missus calling me for dinner - I'd forgotten
we had a stew going in the slow cooker, and she can't make
dumplings to save her life :)

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

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,
which when called, returns fst + snd.

Hope that makes sense.

Cheers,

Jon.

Steven D'Aprano

3/13/2010 7:48:00 PM

0

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