编程在Python中创建算术特殊的方法,(又名工厂函数HOWTO)(Programmatically

2019-09-17 16:13发布

我的想法是创建一个可以概括/减去特定函数的对象/ ...一起,返回一个具有相同属性的新函数对象。 这个例子的代码,希望表明的想法:

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)

我觉得我失去了一些重要的东西,但我无法看到它。

编辑

迪特里希的回答后,我忘了提一个角落的情况。 假设我要继承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)

在这种情况下,有一些更多的工作要做。

Answer 1:

有很多在这里的代码,不需要存在,它的复杂得多,它需要的。

例如, __class__属性是所谓的“神奇”的属性之一。 魔法属性是特殊的,只需要在特殊情况下使用,当您使用元编程等。 有没有必要在这里创造的代码的类。

另一个例子是Func在你的代码的类,它实际上并没有做任何事情。 您可以放心地将其替换为:

def Func(x):
    return x

所以,你有相反的问题:你是不是“失踪”的事情,你有太多的方式。

class Func(object):
    def __init__(self, func):
        self._func = func
    def __call__(self, x):
        return self._func(x)
    def __mul__(self, other):
        return Func(lambda x: self(x) * other(x))
    def __add__(self, other):
        return Func(lambda x: self(x) + other(x))
    def __sub__(self, other):
        return Func(lambda x: self(x) - other(x))

请注意,这是没有办成这样的问题的传统方式。 传统上,一个lambda表达式避免了在这里使用一个表达式树。 使用表达式树的优点在于,所得到的表达式可以代数操纵。 例如,您可以解决这些问题,计算精确的衍生物,或打印出来作为方程。



Answer 2:

我假设你想要f(x ** 2) + f(x ** 3)返回一个函数x**2 + x**3 ? 你可以试试这个:

class Func:
    def __init__(self, func):
        self._func = func

    def __call__(self, *args):
        return self._func(*args)

    def __add__(self, other):
        def result(*args):
            return self._func(*args) + other(*args)
        return Func(result)
    __radd__ = __add__

    def __mul__(self, other):
        def result(*args):
            return self._func(*args) * other(*args)
        return Func(result)
    __rmul__ = __mul__

    # etc...

这对我的作品,是不是你有什么要简单得多。

编辑:

你也许不因打扰self._func在运算方法的调用,并调用self直接。



文章来源: Programmatically create arithmetic special methods in Python, (aka factory function HOWTO)