1

我正在尝试使用 numpy v1.13 中引入的 __array_ufunc__ 方法来实现 numpy 的 ufunc 以使用类。

为简化起见,该类可能如下所示:

class toto():
    def __init__(self, value, name):
        self.value = value
        self.name = name
    def __add__(self, other):
    """add values and concatenate names"""
        return toto(self.value + other.value, self.name + other.name)
    def __sub__(self, other):
    """sub values and concatenate names"""
        return toto(self.value - other.value, self.name + other.name)  

tata = toto(5, "first")  
titi = toto(1, "second")

现在,如果我尝试在这两者之间应用 np.add ,我会得到预期的结果,因为 np.add 依赖于add。但是如果我打电话说 np.exp,我会得到一个预期的错误:

>>> np.exp(tata)
AttributeError: 'toto' object has no attribute 'exp'

现在我想做的是“覆盖”所有 numpy ufunc 以在此类中顺利工作,而无需重新定义类中的每个方法(exp(self)、log(self)、...)。

我打算使用 numpy ufunc 的 [__array_ufunc__] 1来执行此操作,但我不太了解该文档,因为它没有提供简单的实现示例。

如果有人对这个看起来很有希望的新功能有任何经验,你能提供一个简单的例子吗?

4

1 回答 1

1

__array_ufunc__如果我使用方法(和)扩展您的课程__repr__

class toto():
    def __init__(self, value, name):
        self.value = value
        self.name = name
    def __add__(self, other):
        """add values and concatenate names"""
        return toto(self.value + other.value, self.name + other.name)
    def __sub__(self, other):
        """sub values and concatenate names"""
        return toto(self.value - other.value, self.name + other.name)

    def __repr__(self):
        return f"toto: {self.value}, {self.name}"
    def __array_ufunc__(self, *args, **kwargs):
        print(args)
        print(kwargs)

并尝试一些ufunc电话:

In [458]: np.exp(tata)                                                          
(<ufunc 'exp'>, '__call__', toto: 5, first)
{}
In [459]: np.exp.reduce(tata)                                                   
(<ufunc 'exp'>, 'reduce', toto: 5, first)
{}
In [460]: np.multiply.reduce(tata)                                              
(<ufunc 'multiply'>, 'reduce', toto: 5, first)
{}
In [461]: np.exp.reduce(tata,axes=(1,2))                                        
(<ufunc 'exp'>, 'reduce', toto: 5, first)
{'axes': (1, 2)}
In [463]: np.exp.reduce(tata,axes=(1,2),out=np.arange(3))                       
(<ufunc 'exp'>, 'reduce', toto: 5, first)
{'axes': (1, 2), 'out': (array([0, 1, 2]),)}

这显示了您的班级收到的信息。显然你可以做你想做的事。它可以返回NotImplemented。我想在您的情况下,它可以将第一个参数应用于您的self.value,或进行一些自定义计算。

例如,如果我添加

      val = args[0].__call__(self.value) 
      return toto(val, self.name) 

我得到:

In [468]: np.exp(tata)                                                          
(<ufunc 'exp'>, '__call__', toto: 5, first)
{}
Out[468]: toto: 148.4131591025766, first
In [469]: np.sin(tata)                                                          
(<ufunc 'sin'>, '__call__', toto: 5, first)
{}
Out[469]: toto: -0.9589242746631385, first

但是,如果我将对象放入数组中,我仍然会收到方法错误

In [492]: np.exp(np.array(tata))                                                
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-492-4dc37eb0ffe3> in <module>
----> 1 np.exp(np.array(tata))

AttributeError: 'toto' object has no attribute 'exp'

显然ufunc,在对象 dtype 数组上迭代数组的元素,期望使用“相关”方法。对于np.add(+),它会查找__add__方法。因为np.exp它寻找一种exp方法。这__array_ufunc__不叫。

所以看起来它更多地用于 的子类ndarray或等效的东西。我认为,您正在尝试实现一个可以用作对象 dtype 数组元素的类。

于 2019-03-28T01:22:12.610 回答