Note that there are some explanatory texts on larger screens.

plurals
  1. POProgrammatically create arithmetic special methods in Python, (aka factory function HOWTO)
    primarykey
    data
    text
    <p>My idea is to create particular function objects that can be summed/subtracted/... together, returning a new function object that has the same properties. This example code, hopefully, demonstrates the idea:</p> <pre><code>from FuncObj import Func # create some functions quad = Func(lambda x: x**2) cube = Func(lambda x: x**3) # now combine functions as you like plus = quad + cube minus = quad - cube other = quad * quad / cube # and these can be called plus(1) + minus(32) * other(5) </code></pre> <p>I have written the following code, which is hopefully commented and documented enough to explain what i want to achieve.</p> <pre><code>import operator class GenericFunction(object): """ Base class providing arithmetic special methods. Use derived class which must implement the __call__ method. """ # this way of defining special methods works well def __add__(self, operand): """ This is an example of a special method i want to implement. """ obj = GenericFunction() # this is a trick from Alex Martelli at # http://stackoverflow.com/questions/1705928/problem-with-making-object-callable-in-python # to allow per-instance __call__ methods obj.__class__ = type(obj.__class__.__name__, (obj.__class__,), {}) obj.__class__.__call__ = lambda s, ti: self(ti) + operand(ti) return obj # on the other hand this factory function seems buggy def _method_factory(operation, name): """ Method factory. Parameters ---------- op : callable an arithmetic operator from the operator module name : str the name of the special method that will be created Returns ------- method : callable the __***__ special method """ def method(s, operand): obj = GenericFunction() obj.__class__ = type(obj.__class__.__name__, (obj.__class__,), {}) obj.__class__.__call__ = lambda s, ti: operation(s(ti), operand(ti)) return obj return method __sub__ = _method_factory(operator.__sub__, '__sub__') __mul__ = _method_factory(operator.__mul__, '__mul__') __truediv__ = _method_factory(operator.__truediv__, '__div__') class Func(GenericFunction): """ A customizable callable object. Parameters ---------- func : callable """ def __init__(self, func): self.func = func def __call__(self, *args): return self.func(*args) if __name__ == '__main__': # create some functions quad = Func(lambda x: x**2) cube = Func(lambda x: x**3) # now combine functions poly_plus = quad + cube poly_minus = quad - cube # this is the expected behaviour, and it works well # since the __add__ method is defined correctly. assert quad(1) + cube(1) == poly_plus(1) # this, and the others with * and / result in a "maximum recursion depth exceeded" assert quad(1) - cube(1) == poly_minus(1) </code></pre> <p>I think I'm missing something important but I can't see it.</p> <p><strong>EDIT</strong> </p> <p>After Dietrich answer i forgot to mention a corner case. Suppose i want to subclass GenericInput and i need to customize the <strong>call</strong> method__, without passing a callable to the constructor. I have to examples, (actually this is the code for which i originally posted this question).</p> <pre><code>class NoiseInput(GenericInput): def __init__(self, sigma, a, b, t): """ A band-pass noisy input. """ self._noise = lfilter(b, a, np.random.normal(0, 1, len(t))) self._noise *= sigma/self._noise.std() self._spline = InterpolatedUnivariateSpline(t, self._noise, k=2) def __call__(self, ti): """ Compute value of the input at a given time. """ return self._spline(ti) class SineInput(GenericInput): def __init__(self, A, fc): self.A = A self.fc = fc def __call__(self, ti): return self.A*np.sin(2*np.pi*ti*self.fc) </code></pre> <p>In this case there is some more work to do.</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload