Decorator to inject function into __call__ of a class

J

Jon Clements

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.
 
P

Patrick Maupin

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
 
J

Jon Clements

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.
 
J

Jack Diederich

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
 
P

Patrick Maupin

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 wrapper(func):
.... def go():
.... return func(*args, **kw)
.... return go
.... return wrapper
........ def add(a, b):
.... return a + b
....42
 
J

Jon Clements

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.
 
J

Jack Diederich

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
 
J

Jon Clements

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.
 
S

Steven D'Aprano

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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top