当索引看起来像数字的内容时,Python 中的“三个点”是什么意思?
- 2025-04-16 08:57:00
- admin 原创
- 13
问题描述:
下面是什么意思x[...]
?
a = np.arange(6).reshape(2,3)
for x in np.nditer(a, op_flags=['readwrite']):
x[...] = 2 * x
解决方案 1:
虽然建议的重复文章“Python 省略号对象有什么作用?”在一般情况下回答了这个问题,但我认为,在循环python
中使用它需要添加信息。nditer
https://docs.scipy.org/doc/numpy/reference/arrays.nditer.html#modifying-array-values
Python 中的常规赋值操作只是更改本地或全局变量字典中的引用,而不是直接修改现有变量。这意味着,简单地对 x 赋值并不会将值放入数组元素中,而是将 x 从数组元素引用切换为对所赋值的引用。要真正修改数组元素,应该使用省略号对 x 进行索引。
该部分包含您的代码示例。
所以用我的话来说,就地 x[...] = ...
修改会破坏与变量的链接,而不是改变它。它类似于但适用于任何维度(包括 0 维)的数组。在这种情况下,它不仅仅是一个数字,而是一个数组。x
`x = ...nditer
x[:] = ...`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
然后是一维数组,可以使用x[:]=...
或进行修改x[...]=...
。
如果我添加external_loop
下一节中的标志,它x
现在是一个一维数组,并且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,)
比较这个简单的行迭代(在二维数组上):
In [675]: for x in a:
...: print(x, x.shape)
...: x[:] = 2 * x
...:
[ 0 8 16] (3,)
[24 32 40] (3,)
这将对的行(第一维)进行迭代a
。 x
然后是一维数组,可以使用x[:] = ...
或进行修改x[...] = ...
。
请通读本页并进行实验,nditer
直至最后。 本身nditer
在 中用处不大python
。它不会加速迭代——除非你将代码移植到cython
。np.ndindex
是少数numpy
使用 的非编译函数之一nditer
。
解决方案 2:
省略号的...
意思是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])
这使得每次只操作一个维度变得容易。
有一件事 - 在任何给定的索引表达式中只能有一个省略号,否则您的表达式对于:
每个表达式中应该放入多少个省略号会产生歧义。
解决方案 3:
我相信一个很好的类比(大多数人可能已经习惯了)是这样的思考:
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,...]
是无效的。
解决方案 4:
无需长篇解释:-) 在 Numpy 中,省略号(3 个点)仅表示“索引与数组形状匹配所需的数量:
” 。
:
本身是表示所考虑维度中所有元素的快捷方式。
例如,对于一个形状(n, p, q, r, s)
,即一个具有 5 个维度的数组,可以使用省略号来替换 0、1、2、3、4 或 5 个连续的:
。这些形式是等效的:
a[:, :, :, 4, 2]
和a[..., 4, 2]
a[4, :, :, :, 2]
和a[4, ..., 2]
a[4, 2, :, :, :]
和a[4, 2, ...]
a[:, :, :, :, 4]
和a[..., 4]
a[:, :, :, :, :]
和a[...]
以及任何带有如下切片的变体:
a[1:2, :, :, :, 2:6]
和a[1:2, ..., 2:6]
这可以用来简化返回数组切片的索引,而不是使用连续的索引:
来包含相应的整个维度。这种简化隐藏了数组的实际形状,并可能导致切片错误。
确实,索引中只能有一个省略号,否则 Numpy 如何知道如何分配所需的省略号:
?
扫码咨询,免费领取项目管理大礼包!