Моя идея - создать определенные объекты функций, которые можно суммировать/вычитать/... вместе, возвращая новый объект функции с теми же свойствами. Этот пример кода, надеюсь, демонстрирует идею:Программно создавать арифметические специальные методы в Python, (как фабричная функция HOWTO)
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)
Я написал следующий код, который мы надеемся, документированных достаточно, чтобы объяснить, что я хочу достичь.
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)
Я думаю, что у меня что-то не хватает, но я не вижу его.
EDIT
После Дитриха ответа я забыл упомянуть угловой случай. Предположим, что я хочу подкласс GenericInput, и мне нужно настроить вызов method__, не передавая вызываемому конструктору. У меня есть примеры (на самом деле это код, для которого я изначально разместил этот вопрос).
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)
В этом случае есть еще одна работа.
в вашем примере означает 'plus (1)' mean 'quad (1) + cube (1)'? – inspectorG4dget
@ inspectorG4dget Да, это намеченное поведение. – Davide