Что означают «три точки» в Python при индексировании того, что выглядит как число?

87

Что означает x[...]ниже?

a = np.arange(6).reshape(2,3)
for x in np.nditer(a, op_flags=['readwrite']):
    x[...] = 2 * x
Нан Хуа
источник
1
Это не список.
user2357112 поддерживает Монику
2
Возможный дубликат того, что делает объект Python Ellipsis?
Лукаш Рогальский
1
Должен быть забавный способ понять, что это выступление Джеймса Пауэлла youtube.com/watch?v=65_-6kEAq58
SARose

Ответы:

69

Хотя предлагаемый дубликат Что делает объект 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это не просто число, это массив.

Возможно, наиболее близким к этой 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,)

это итерация по строкам (1-й размер) a. 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,)

Сравните эту простую итерацию строки (в двумерном массиве):

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

это итерация по строкам (1-й размер) a. xтогда является 1d-массивом и может быть изменен с помощью x[:] = ...или x[...] = ....

Прочтите и поэкспериментируйте с этой nditerстраницей до конца. Сам по себе nditerне так уж и полезен python. Это не ускоряет итерацию - пока вы не перенесете свой код в cython. np.ndindex- одна из немногих нескомпилированных numpyфункций, использующих nditer.

hpaulj
источник
Обратите внимание, что такие вещи, как x [1,:, ...] также являются допустимым синтаксисом. Оставлено на будущее.
borgr