¿Qué es :: (dos puntos dobles) en Python cuando se suscriben secuencias?

Sé que puedo usar algo como la string[3:4] para obtener una subcadena en Python, pero ¿qué significa el 3 en somesequence[::3] ?

significa “nada para el primer argumento, nada para el segundo, y salte a las tres”. Obtiene cada tercer elemento de la secuencia cortada. Rebanadas extendidas es lo que quieres. Nuevo en Python 2.3

Las direcciones de segmentos de secuencia de Python se pueden escribir como [inicio: final: paso] y cualquiera de los de inicio, parada o final se puede eliminar. a[::3] es cada tercer elemento de la secuencia.

seq[::n] es una secuencia de cada n -ésimo elemento en toda la secuencia.

Ejemplo:

 >>> range(10)[::2] [0, 2, 4, 6, 8] 

La syntax es:

 seq[start:end:step] 

Así que puedes hacer:

 >>> range(100)[5:18:2] [5, 7, 9, 11, 13, 15, 17] 

Explicación

s[i:j:k] es, según la documentación , “corte de s de i a j con el paso k”. Cuando i y j están ausentes, se asume toda la secuencia y, por lo tanto, s[::k] significa “cada elemento k-th”.

Ejemplos

Primero, inicialicemos una lista:

 >>> s = range(20) >>> s [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] 

Tomemos cada artículo 3 de s :

 >>> s[::3] [0, 3, 6, 9, 12, 15, 18] 

Tomemos cada artículo 3 de s[2:] :

 >>> s[2:] [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] >>> s[2::3] [2, 5, 8, 11, 14, 17] 

Tomemos cada artículo 3 de s[5:12] :

 >>> s[5:12] [5, 6, 7, 8, 9, 10, 11] >>> s[5:12:3] [5, 8, 11] 

Tomemos cada artículo 3 de s[:10] :

 >>> s[:10] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> s[:10:3] [0, 3, 6, 9] 

TL; DR

Este ejemplo visual le mostrará cómo seleccionar cuidadosamente los elementos en una Matriz NumPy (matriz bidimensional) de una manera bastante entretenida (lo prometo). El paso 2 a continuación ilustra el uso de esos “dos puntos dobles” :: en cuestión.

(Precaución: este es un ejemplo específico de matriz NumPy con el objective de ilustrar un caso de uso de “dos puntos dobles” :: para saltar elementos en varios ejes. Este ejemplo no cubre las estructuras de datos nativas de Python como la List ).

Un ejemplo concreto para gobernarlos a todos …

Digamos que tenemos una matriz NumPy que se ve así:

 In [1]: import numpy as np In [2]: X = np.arange(100).reshape(10,10) In [3]: X Out[3]: 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, 24, 25, 26, 27, 28, 29], [30, 31, 32, 33, 34, 35, 36, 37, 38, 39], [40, 41, 42, 43, 44, 45, 46, 47, 48, 49], [50, 51, 52, 53, 54, 55, 56, 57, 58, 59], [60, 61, 62, 63, 64, 65, 66, 67, 68, 69], [70, 71, 72, 73, 74, 75, 76, 77, 78, 79], [80, 81, 82, 83, 84, 85, 86, 87, 88, 89], [90, 91, 92, 93, 94, 95, 96, 97, 98, 99]]) 

Por alguna razón, su jefe quiere que seleccione los siguientes elementos:

introduzca la descripción de la imagen aquí

“¿Pero cómo?” … ¡Sigue leyendo! (Podemos hacer esto en un enfoque de 2 pasos)

Paso 1 – Obtener subconjunto

Especifique el “índice de inicio” y el “índice de finalización” en las direcciones de fila y de columna.

introduzca la descripción de la imagen aquí

En codigo:

 In [5]: X2 = X[2:9,3:8] In [6]: X2 Out[6]: array([[23, 24, 25, 26, 27], [33, 34, 35, 36, 37], [43, 44, 45, 46, 47], [53, 54, 55, 56, 57], [63, 64, 65, 66, 67], [73, 74, 75, 76, 77], [83, 84, 85, 86, 87]]) 

Observe que ahora acabamos de obtener nuestro subconjunto, con el uso de la técnica de indexación simple de inicio y fin. A continuación, cómo hacer ese “salto” … (sigue leyendo)

Paso 2 – Seleccione los elementos (con el argumento “salto paso”)

Ahora podemos especificar los “pasos de salto” en las direcciones de fila y de columna (para seleccionar elementos de una forma de “salto”) como esto:

introduzca la descripción de la imagen aquí

En código (note los dos puntos dobles):

 In [7]: X3 = X2[::3, ::2] In [8]: X3 Out[8]: array([[23, 25, 27], [53, 55, 57], [83, 85, 87]]) 

Acabamos de seleccionar todos los elementos según sea necesario! 🙂

Consolidar el Paso 1 (inicio y final) y el Paso 2 (“saltar”)

Ahora que conocemos el concepto, podemos combinar fácilmente el paso 1 y el paso 2 en un paso consolidado: para la compacidad:

 In [9]: X4 = X[2:9,3:8][::3,::2] In [10]: X4 Out[10]: array([[23, 25, 27], [53, 55, 57], [83, 85, 87]]) 

¡Hecho!

Cuando se corta en Python, el tercer parámetro es el paso. Como otros mencionaron, vea Rebanadas extendidas para una buena descripción.

Con este conocimiento, [::3] solo significa que no ha especificado ningún índice de inicio o fin para su sector. Como ha especificado un paso, 3 , esto tomará cada tercera entrada de something comience en el primer índice. Por ejemplo:

 >>> '123123123'[::3] '111' 

El tercer parámetro es el paso. Entonces [:: 3] devolvería cada tercer elemento de la lista / cadena.

Python usa el :: para separar los valores de Fin, Inicio y Paso.

También puedes usar esta notación en tus propias clases personalizadas para hacer que haga lo que quieras

 class C(object): def __getitem__(self, k): return k # Single argument is passed directly. assert C()[0] == 0 # Multiple indices generate a tuple. assert C()[0, 1] == (0, 1) # Slice notation generates a slice object. assert C()[1:2:3] == slice(1, 2, 3) # If you omit any part of the slice notation, it becomes None. assert C()[:] == slice(None, None, None) assert C()[::] == slice(None, None, None) assert C()[1::] == slice(1, None, None) assert C()[:2:] == slice(None, 2, None) assert C()[::3] == slice(None, None, 3) # Tuple with a slice object: assert C()[:, 1] == (slice(None, None, None), 1) # Ellipsis class object. assert C()[...] == Ellipsis 

Entonces podemos abrir objetos de corte como:

 s = slice(1, 2, 3) assert s.start == 1 assert s.stop == 2 assert s.step == 3 

Esto se usa notablemente en Numpy para cortar matrices multidimensionales en cualquier dirección.

Por supuesto, cualquier API sana debe usar ::3 con la semántica habitual “cada 3”.