避免 numpy 为重载运算符分发操作
Avoid numpy distributing an operation for overloaded operator
默认情况下,如果 numpy 不知道其他对象的类型,它会跨数组分配操作。这在大多数情况下效果很好。例如,以下行为符合预期。
np.arange(5) + 5 # = [5, 6, 7, 8, 9]
我想定义一个 class 来覆盖加法运算符,如下面的代码所示。
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 分发操作?我想避免使用 subclassing numpy 数组。
对于每个不太急切的 np.ndarray
和 subclasses(例如在早期的 numpy 版本中 np.ma.MaskedArray
忽略它)你可以定义 __array_priority__
甚至如果你不直接subclass np.ndarray
.
这背后的想法很简单:具有较高优先级的子class决定了哪个运算符定义了数学运算而不是运算的顺序。
你的工作示例 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
机器的 class。
默认情况下,如果 numpy 不知道其他对象的类型,它会跨数组分配操作。这在大多数情况下效果很好。例如,以下行为符合预期。
np.arange(5) + 5 # = [5, 6, 7, 8, 9]
我想定义一个 class 来覆盖加法运算符,如下面的代码所示。
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 分发操作?我想避免使用 subclassing numpy 数组。
对于每个不太急切的 np.ndarray
和 subclasses(例如在早期的 numpy 版本中 np.ma.MaskedArray
忽略它)你可以定义 __array_priority__
甚至如果你不直接subclass np.ndarray
.
这背后的想法很简单:具有较高优先级的子class决定了哪个运算符定义了数学运算而不是运算的顺序。
你的工作示例 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
机器的 class。