【问题标题】:Avoid numpy distributing an operation for overloaded operator避免 numpy 为重载运算符分发操作
【发布时间】:2016-03-08 11:57:14
【问题描述】:

默认情况下,如果 numpy 不知道另一个对象的类型,它会跨数组分配操作。这在大多数情况下都很有效。例如,以下行为符合预期。

np.arange(5) + 5 # = [5, 6, 7, 8, 9]

我想定义一个覆盖加法运算符的类,如下面的代码所示。

class Example:
    def __init__(self, value):
        self.value = value

    def __add__(self, other):
        return other + self.value

    def __radd__(self, other):
        return other + self.value

它适用于标量值。例如,

np.arange(5) + Example(5) # = [5, 6, 7, 8, 9]

但是,它并不能完全满足我对向量值的要求。例如,

np.arange(5) + Example(np.arange(5)) 

产生输出

array([array([0, 1, 2, 3, 4]), array([1, 2, 3, 4, 5]),
   array([2, 3, 4, 5, 6]), array([3, 4, 5, 6, 7]),
   array([4, 5, 6, 7, 8])], dtype=object)

因为前面 numpy 数组的 __add__ 运算符优先于我定义的 __radd__ 运算符。 Numpy 的 __add__ 运算符为 numpy 数组的每个元素调用 __radd__,从而产生一个数组数组。如何避免 numpy 分发操作?我想避免子类化 numpy 数组。

【问题讨论】:

    标签: python numpy operator-overloading


    【解决方案1】:

    对于每个 np.ndarray 和不太急切的子类(例如,在早期的 numpy 版本中,np.ma.MaskedArray 忽略它)你可以定义 __array_priority__,即使你不直接继承 np.ndarray。

    这背后的想法很简单:具有较高优先级的子类决定哪个运算符定义数学运算,而不是运算的顺序。

    Example 的工作示例是:

    class Example:
    
        # Define this priority
        __array_priority__ = 2
    
        def __init__(self, value):
            self.value = value
    
        def __add__(self, other):
            return other + self.value
    
        def __radd__(self, other):
            return other + self.value
    
    
    import numpy as np
    np.arange(5) + Example(np.arange(5)) 
    # returns array([0, 2, 4, 6, 8])
    

    所以它可以按需要工作。但是请注意,在依赖这种方法时存在一些微妙的问题:

    它不适用于 MaskedArrays,因为它们的优先级为 15(因此您需要将优先级更改为 16+ 才能使其工作):

    import numpy as np
    np.ma.array(np.arange(5)) + Example(np.arange(5)) 
    
    # returns:
    masked_array(data = [array([0, 1, 2, 3, 4]) array([1, 2, 3, 4, 5])    array([2, 3, 4, 5, 6])
    array([3, 4, 5, 6, 7]) array([4, 5, 6, 7, 8])],
             mask = False,
       fill_value = ?)
    

    例如,它不适用于astropy.units.Quantity,因为它们已将优先级定义为10000:

    import astropy.units as u
    (np.arange(5)*u.dimensionless_unscaled) + Example(np.arange(5)) 
    #returns:
    <Quantity [array([ 0.,  1.,  2.,  3.,  4.]),
               array([ 1.,  2.,  3.,  4.,  5.]),
               array([ 2.,  3.,  4.,  5.,  6.]),
               array([ 3.,  4.,  5.,  6.,  7.]),
               array([ 4.,  5.,  6.,  7.,  8.])]>
    

    而且它不适用于任何不使用numpy-machinery 的类。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-11
      • 2016-06-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-18
      相关资源
      最近更新 更多