O que “três pontos” em Python significam ao indexar o que parece um número?

86

Qual é o significado de x[...]abaixo?

a = np.arange(6).reshape(2,3)
for x in np.nditer(a, op_flags=['readwrite']):
    x[...] = 2 * x
Nan Hua
fonte
1
Isso não é uma lista.
user2357112 suporta Monica
2
Possível duplicata de O que o objeto Python Ellipsis faz?
Łukasz Rogalski
1
Uma maneira divertida de entender isso é a palestra de James Powell youtube.com/watch?v=65_-6kEAq58
SARose 01 de

Respostas:

68

Enquanto a duplicata proposta , o que o objeto Python Ellipsis faz? responde à pergunta em um pythoncontexto geral , seu uso em um nditerloop requer, eu acho, informações adicionais.

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

A atribuição regular em Python simplesmente altera uma referência no dicionário de variável local ou global em vez de modificar uma variável existente no local. Isso significa que simplesmente atribuir ax não colocará o valor no elemento da matriz, mas, em vez disso, mudará x de uma referência de elemento da matriz para uma referência ao valor atribuído. Para realmente modificar o elemento da matriz, x deve ser indexado com reticências.

Essa seção inclui seu exemplo de código.

Então, em minhas palavras, o x[...] = ...modifica xno local; x = ...teria quebrado o link para a nditervariável e não o alterado. É semelhante, x[:] = ...mas funciona com matrizes de qualquer dimensão (incluindo 0d). Nesse contexto, xnão é apenas um número, é um array.

Talvez a coisa mais próxima desta nditeriteração, sem 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]])

Observe que tive que indexar e modificar a[i]diretamente. Eu não poderia ter usado x = 2*x,. Nesta iteração xé um escalar e, portanto, não mutável

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

Mas no nditercaso xé um array 0d e mutável.

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'> ()
...

E porque é 0d, x[:]não pode ser usado em vez dex[...]

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

Uma iteração de array mais simples também pode fornecer informações:

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

isso itera nas linhas (1ª dimensão) de a. xé então uma matriz 1d e pode ser modificada com x[:]=...ou x[...]=....

E se eu adicionar o external_loopsinalizador da próxima seção , xagora é um array 1d e x[:] =funcionaria. Mas x[...] =ainda funciona e é mais geral. x[...]é usado todos os outros nditerexemplos.

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,)

Compare esta iteração de linha simples (em uma matriz 2d):

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

isso itera nas linhas (1ª dimensão) de a. xé então uma matriz 1d e pode ser modificada com x[:] = ...ou x[...] = ....

Leia e experimente esta nditerpágina do começo ao fim. Por si só, nditernão é tão útil em python. Isso não acelera a iteração - não até que você transporte seu código para cython. np.ndindexé uma das poucas numpyfunções não compiladas que usa nditer.

hpaulj
fonte
Observe que coisas como x [1,:, ...] também são uma sintaxe permitida. Deixado para referência futura.
borgr