当索引一个看起来像数字的东西时,Python 中的“三个点”意味着什么?

下面的 x[...]是什么意思?

a = np.arange(6).reshape(2,3)
for x in np.nditer(a, op_flags=['readwrite']):
x[...] = 2 * x
117854 次浏览

虽然提议的重复 Python Ellipsis 对象做什么?在一般 python上下文中回答了这个问题,但是我认为,在 nditer循环中使用它需要添加信息。

Https://docs.scipy.org/doc/numpy/reference/arrays.nditer.html#modifying-array-values

Python 中的常规赋值只是更改局部或全局变量字典中的引用,而不是就地修改现有变量。这意味着简单地赋值 x 不会将值放入数组元素中,而是将 x 从数组元素引用切换为对所赋值的引用。要实际修改数组的元素,x 应该使用省略号进行索引。

该部分包括您的代码示例。

因此,用我的话说,x[...] = ...就地修改 x; x = ...会中断到 nditer变量的链接,而不会更改它。它类似于 x[:] = ...,但是可以处理任何维度的数组(包括0d)。在这种情况下,x不仅仅是一个数字,它还是一个数组。

也许没有 nditernditer迭代最接近的事情是:

In [667]: for i, x in np.ndenumerate(a):
...:     print(i, x)
...:     a[i] = 2 * x
...:
(0, 0) 0
(0, 1) 1
...
(1, 2) 5
In [668]: a
Out[668]:
array([[ 0,  2,  4],
[ 6,  8, 10]])

注意,我必须直接索引和修改 a[i]。我不能使用,x = 2*x。在这个迭代中,x是一个标量,因此是不可变的

In [669]: for i,x in np.ndenumerate(a):
...:     x[...] = 2 * x
...
TypeError: 'numpy.int32' object does not support item assignment

但是在 nditer的情况下,x是一个0d 数组,并且是可变的。

In [671]: for x in np.nditer(a, op_flags=['readwrite']):
...:     print(x, type(x), x.shape)
...:     x[...] = 2 * x
...:
0 <class 'numpy.ndarray'> ()
4 <class 'numpy.ndarray'> ()
...

因为它是0d,所以不能用 x[:]代替 x[...]

----> 3     x[:] = 2 * x
IndexError: too many indices for array

一个更简单的数组迭代可能也有助于洞察:

In [675]: for x in a:
...:     print(x, x.shape)
...:     x[:] = 2 * x
...:
[ 0  8 16] (3,)
[24 32 40] (3,)

这将迭代 a的行(第一个 dim)。然后 x是一个1d 数组,可以用 x[:]=...x[...]=...进行修改。

如果我从下一个 部分添加 external_loop标志,x现在是一个1d 数组,而 x[:] =将工作。但是 x[...] =仍然可以工作,而且更加通用。x[...]是使用所有其他 nditer的例子。

In [677]: for x in np.nditer(a, op_flags=['readwrite'], flags=['external_loop']):
...:     print(x, type(x), x.shape)
...:     x[...] = 2 * x
[ 0 16 32 48 64 80] <class 'numpy.ndarray'> (6,)

比较下面这个简单的行迭代(在2d 数组上) :

In [675]: for x in a:
...:     print(x, x.shape)
...:     x[:] = 2 * x
...:
[ 0  8 16] (3,)
[24 32 40] (3,)

这将迭代 a的行(第一个 dim)。然后 x是一个1d 数组,可以用 x[:] = ...x[...] = ...进行修改。

阅读和实验这个 nditer页所有的方式,直到最后。就其本身而言,nditerpython中并不那么有用。它不会加速迭代——直到您将代码移植到 cython.np.ndindex是少数几个使用 nditer的未编译的 numpy函数之一。

省略号 ...表示 as many : as needed

对于那些没有时间的人,这里有一个简单的例子:

In [64]: X = np.reshape(np.arange(9), (3,3))


In [67]: Y = np.reshape(np.arange(2*3*4), (2,3,4))


In [70]: X
Out[70]:
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])


In [71]: X[:,0]
Out[71]: array([0, 3, 6])


In [72]: X[...,0]
Out[72]: array([0, 3, 6])


In [73]: Y
Out[73]:
array([[[ 0,  1,  2,  3],
[ 4,  5,  6,  7],
[ 8,  9, 10, 11]],


[[12, 13, 14, 15],
[16, 17, 18, 19],
[20, 21, 22, 23]]])


In [74]: Y[:,0]
Out[74]:
array([[ 0,  1,  2,  3],
[12, 13, 14, 15]])


In [75]: Y[...,0]
Out[75]:
array([[ 0,  4,  8],
[12, 16, 20]])


In [76]: X[0,...,0]
Out[76]: array(0)


In [77]: Y[0,...,0]
Out[77]: array([0, 4, 8])

这使得一次只操作一个维度变得很容易。

一件事——在任何给定的索引表达式中,只能有一个省略号,否则表达式在每个表达式中应该放入多少 :的问题上将是模棱两可的。

我相信一个很好的类比(大多数人可能已经习惯了)就是这样想:

import numpy as np


random_array = np.random.rand(2, 2, 2, 2)

在这种情况下,[:, :, :, 0] and [..., 0]是相同的。

你只能用它来分析一个特定的维度,比如说你有一批50张128x128 RGB 的图片(50,3,128,128) ,如果你想在每个颜色通道的每张图片上切片,你可以选择 image[:,:,50:70, 20:80] or image[...,50:70,20:80]

只是要注意,在 [...,0,...]无效这样的语句中,您不能多次使用它。