Python中的“三个点”在索引看起来像数字时是什么意思?

时间:2017-02-12 17:24:29

标签: python numpy iterator

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

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

2 个答案:

答案 0 :(得分:39)

虽然建议的重复What does the Python Ellipsis object do?在一般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不仅仅是一个数字,它还是一个数组。

在没有nditer的情况下,与此nditer次迭代最接近的可能是:

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的行(第一个暗淡)。 x是一个1d数组,可以使用x[:]=...x[...]=...进行修改。

如果我从下一个section添加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的行(第一个暗淡)。 x是一个1d数组,可以使用x[:] = ...x[...] = ...进行修改。

阅读并试用此nditer页面一直到最后。 nditer本身在python中没有用处。它不会加速迭代 - 直到您将代码移植到cythonnp.ndindex是少数使用numpy的非编译nditer函数之一。

答案 1 :(得分:1)

省略号 ... 表示 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])

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

有一点 - 在任何给定的索引表达式中您只能有一个省略号,否则您的表达式对于每个应该放入多少个 : 会产生歧义。