python 列表 + 空 numpy 数组 = 空 numpy 数组?
python list + empty numpy array = empty numpy array?
今天我注意到我的代码有些奇怪,并发现在某些情况下它 运行 归结为执行以下内容:
my_list = [0] + np.array([])
这导致 my_list
如下:
array([], dtype=float64)
一开始我很困惑,后来我了解到解释器首先将列表转换为 numpy 数组,然后尝试进行广播操作:
>>> np.array([0]) + np.array([])
array([], dtype=float64)
我对此行为有一些疑问:
- 为什么要广播?
- 如果 python 扔一个
错误,至少对于转换和制作列表的这种特殊情况
消失?
感谢您的澄清!
首先:
Wouldn't it be better if python threw an error, at least for this particular case where a list is converted and made disappear?
我不认为测试是可能的。根据to this comment:
For reversed operations like b.__radd__(a)
we call the corresponding ufunc.
这意味着使用 [0] + np.array([])
实际上会调用 ufunc np.add([0], np.array([]))
,它将类似数组的列表转换为数组,而没有机会决定操作数的大小。
所以广播是给定的。那么问题是将形状 (1,)
和 (0,)
广播到 (0,)
是否明智。你可以这样想:标量总是广播,1 元素一维数组在大多数情况下和标量一样好:
>>> np.add([0], [])
array([], dtype=float64)
>>> np.add(0, [])
array([], dtype=float64)
如果你这样看,它通常是有道理的,尽管我同意这很令人惊讶,尤其是非等长数组不会像这样广播。但这不是一个错误(只是一个功能的有趣情况)。
更准确地说,广播发生的事情总是"dimensions with size 1 will broadcast"。类数组 [0]
的形状为 (1,)
,而 np.array([])
的形状为 (0,)
(与标量 np.int64()
的形状相反,后者的形状为 ()
!)。所以广播发生在单例上,结果的形状为 (0,)
.
如果我们注入更多的单一维度,它会变得更清楚:
>>> ([0] + np.array([])).shape
(0,)
>>> ([[0]] + np.array([])).shape
(1, 0)
>>> ([[[0]]] + np.array([])).shape
(1, 1, 0)
>>> np.shape([[[0]]])
(1, 1, 1)
因此,例如在最后一种情况下,形状 (1, 1, 1)
将沿其最后一个维度很好地广播一维数组,并且在这种情况下结果确实应该是 (1, 1, 0)
。
今天我注意到我的代码有些奇怪,并发现在某些情况下它 运行 归结为执行以下内容:
my_list = [0] + np.array([])
这导致 my_list
如下:
array([], dtype=float64)
一开始我很困惑,后来我了解到解释器首先将列表转换为 numpy 数组,然后尝试进行广播操作:
>>> np.array([0]) + np.array([])
array([], dtype=float64)
我对此行为有一些疑问:
- 为什么要广播?
- 如果 python 扔一个 错误,至少对于转换和制作列表的这种特殊情况 消失?
感谢您的澄清!
首先:
Wouldn't it be better if python threw an error, at least for this particular case where a list is converted and made disappear?
我不认为测试是可能的。根据to this comment:
For reversed operations like
b.__radd__(a)
we call the corresponding ufunc.
这意味着使用 [0] + np.array([])
实际上会调用 ufunc np.add([0], np.array([]))
,它将类似数组的列表转换为数组,而没有机会决定操作数的大小。
所以广播是给定的。那么问题是将形状 (1,)
和 (0,)
广播到 (0,)
是否明智。你可以这样想:标量总是广播,1 元素一维数组在大多数情况下和标量一样好:
>>> np.add([0], [])
array([], dtype=float64)
>>> np.add(0, [])
array([], dtype=float64)
如果你这样看,它通常是有道理的,尽管我同意这很令人惊讶,尤其是非等长数组不会像这样广播。但这不是一个错误(只是一个功能的有趣情况)。
更准确地说,广播发生的事情总是"dimensions with size 1 will broadcast"。类数组 [0]
的形状为 (1,)
,而 np.array([])
的形状为 (0,)
(与标量 np.int64()
的形状相反,后者的形状为 ()
!)。所以广播发生在单例上,结果的形状为 (0,)
.
如果我们注入更多的单一维度,它会变得更清楚:
>>> ([0] + np.array([])).shape
(0,)
>>> ([[0]] + np.array([])).shape
(1, 0)
>>> ([[[0]]] + np.array([])).shape
(1, 1, 0)
>>> np.shape([[[0]]])
(1, 1, 1)
因此,例如在最后一种情况下,形状 (1, 1, 1)
将沿其最后一个维度很好地广播一维数组,并且在这种情况下结果确实应该是 (1, 1, 0)
。