Comprender la notación de corte

3286

Necesito una buena explicación (las referencias son un plus) sobre la notación de corte de Python.

Para mí, esta notación necesita un poco de recuperación.

Parece extremadamente poderoso, pero aún no tengo la cabeza clara.

Simón
fuente

Respuestas:

4508

Es bastante simple realmente:

a[start:stop]  # items start through stop-1
a[start:]      # items start through the rest of the array
a[:stop]       # items from the beginning through stop-1
a[:]           # a copy of the whole array

También está el stepvalor, que se puede usar con cualquiera de los anteriores:

a[start:stop:step] # start through not past stop, by step

El punto clave a recordar es que el :stopvalor representa el primer valor que no está en el segmento seleccionado. Entonces, la diferencia entre stopy startes el número de elementos seleccionados (si stepes 1, el valor predeterminado).

La otra característica es que starto stoppuede ser un número negativo , lo que significa que cuenta desde el final de la matriz en lugar del principio. Entonces:

a[-1]    # last item in the array
a[-2:]   # last two items in the array
a[:-2]   # everything except the last two items

Del mismo modo, steppuede ser un número negativo:

a[::-1]    # all items in the array, reversed
a[1::-1]   # the first two items, reversed
a[:-3:-1]  # the last two items, reversed
a[-3::-1]  # everything except the last two items, reversed

Python es amable con el programador si hay menos elementos de los que pides. Por ejemplo, si solicita a[:-2]y asolo contiene un elemento, obtendrá una lista vacía en lugar de un error. A veces preferiría el error, por lo que debe tener en cuenta que esto puede suceder.

Relación con el slice()objeto

El operador de corte []se está utilizando en el código anterior con un slice()objeto que usa la :notación (que solo es válida dentro []), es decir:

a[start:stop:step]

es equivalente a:

a[slice(start, stop, step)]

Los objetos de segmento también se comportan de manera ligeramente diferente dependiendo del número de argumentos, de manera similar a range(), es decir, a ambos slice(stop)y slice(start, stop[, step])son compatibles. Para omitir la especificación de un argumento dado, se podría usar None, de modo que, por ejemplo, a[start:]sea ​​equivalente a[slice(start, None)]o a[::-1]sea ​​equivalente a a[slice(None, None, -1)].

Si bien la :notación basada en datos es muy útil para el corte simple, el uso explícito de slice()objetos simplifica la generación programática de corte.

Greg Hewgill
fuente
122
Cortar los tipos incorporados devuelve una copia, pero eso no es universal. En particular, cortar las matrices NumPy devuelve una vista que comparte memoria con el original.
Beni Cherniavsky-Paskin
43
Esta es una hermosa respuesta con los votos para probarlo, pero se pierde una cosa: puede sustituir Nonecualquiera de los espacios vacíos. Por ejemplo, [None:None]hace una copia completa. Esto es útil cuando necesita especificar el final del rango usando una variable y necesita incluir el último elemento.
Mark Ransom
66
Lo que realmente me molesta es que Python dice que cuando no establece el inicio y el final, el valor predeterminado es 0 y la longitud de la secuencia. Entonces, en teoría, cuando usa "abcdef" [:: - 1] debería transformarse en "abcdef" [0: 6: -1], pero estas dos expresiones no obtienen el mismo resultado. Siento que falta algo en la documentación de Python desde la creación del lenguaje.
axell13
66
Y sé que "abcdef" [:: - 1] se transforma en "abcdef" [6: -7: -1], por lo que, la mejor manera de explicar sería: que len ser la longitud de la secuencia. Si el paso es positivo , los valores predeterminados para inicio y fin son 0 y len . De lo contrario, si el paso es negativo , los valores predeterminados para inicio y fin son len y - len - 1.
axell13
44
Dado que stackoverflow.com/questions/39241529/… está marcado como duplicado de esto, ¿tendría sentido agregar una sección con lo que delhace la notación de corte de wrt? En particular, del arr[:]no es inmediatamente obvio ("arr [:] hace una copia, ¿así que borra esa copia ???", etc.)
khazhyk
538

El tutorial de Python habla sobre esto (desplácese hacia abajo un poco hasta llegar a la parte sobre el corte).

El diagrama de arte ASCII también es útil para recordar cómo funcionan los cortes:

 +---+---+---+---+---+---+
 | P | y | t | h | o | n |
 +---+---+---+---+---+---+
 0   1   2   3   4   5   6
-6  -5  -4  -3  -2  -1

Una forma de recordar cómo funcionan las rebanadas es pensar en los índices como apuntando entre caracteres, con el borde izquierdo del primer carácter numerado 0. Luego, el borde derecho del último carácter de una cadena de n caracteres tiene el índice n .

Hans Nowak
fuente
10
Esta sugerencia funciona para un paso positivo, pero no para un paso negativo. Desde el diagrama, espero a[-4,-6,-1]que sea, yPpero lo es ty. Lo que siempre funciona es pensar en caracteres o espacios y usar la indexación como un intervalo medio abierto: derecho abierto si es un paso positivo, izquierdo abierto si es un paso negativo.
aguadopd
Pero no hay forma de colapsar a un conjunto vacío que comienza desde el final (como lo x[:0]hace al comenzar desde el principio), por lo que debe hacer arreglos pequeños en casos especiales. : /
endolith
412

Enumerando las posibilidades permitidas por la gramática:

>>> seq[:]                # [seq[0],   seq[1],          ..., seq[-1]    ]
>>> seq[low:]             # [seq[low], seq[low+1],      ..., seq[-1]    ]
>>> seq[:high]            # [seq[0],   seq[1],          ..., seq[high-1]]
>>> seq[low:high]         # [seq[low], seq[low+1],      ..., seq[high-1]]
>>> seq[::stride]         # [seq[0],   seq[stride],     ..., seq[-1]    ]
>>> seq[low::stride]      # [seq[low], seq[low+stride], ..., seq[-1]    ]
>>> seq[:high:stride]     # [seq[0],   seq[stride],     ..., seq[high-1]]
>>> seq[low:high:stride]  # [seq[low], seq[low+stride], ..., seq[high-1]]

Por supuesto, si (high-low)%stride != 0, entonces el punto final será un poco más bajo que high-1.

Si stridees negativo, el orden cambia un poco ya que estamos haciendo la cuenta regresiva:

>>> seq[::-stride]        # [seq[-1],   seq[-1-stride],   ..., seq[0]    ]
>>> seq[high::-stride]    # [seq[high], seq[high-stride], ..., seq[0]    ]
>>> seq[:low:-stride]     # [seq[-1],   seq[-1-stride],   ..., seq[low+1]]
>>> seq[high:low:-stride] # [seq[high], seq[high-stride], ..., seq[low+1]]

La división extendida (con comas y puntos suspensivos) se usa principalmente solo con estructuras de datos especiales (como NumPy); Las secuencias básicas no son compatibles.

>>> class slicee:
...     def __getitem__(self, item):
...         return repr(item)
...
>>> slicee()[0, 1:2, ::5, ...]
'(0, slice(1, 2, None), slice(None, None, 5), Ellipsis)'
efímero
fuente
En realidad, todavía queda algo, por ejemplo, si escribo 'apple' [4: -4: -1] obtengo 'elp', ¿Python está traduciendo -4 a 1 tal vez?
liyuan 01 de
tenga en cuenta que los backticks están en desuso a favor derepr
wjandrea
@liyuan El tipo de implementación __getitem__es; su ejemplo es equivalente a apple[slice(4, -4, -1)].
chepner
320

Las respuestas anteriores no discuten la asignación de rebanadas. Para comprender la asignación de cortes, es útil agregar otro concepto al arte ASCII:

                +---+---+---+---+---+---+
                | P | y | t | h | o | n |
                +---+---+---+---+---+---+
Slice position: 0   1   2   3   4   5   6
Index position:   0   1   2   3   4   5

>>> p = ['P','y','t','h','o','n']
# Why the two sets of numbers:
# indexing gives items, not lists
>>> p[0]
 'P'
>>> p[5]
 'n'

# Slicing gives lists
>>> p[0:1]
 ['P']
>>> p[0:2]
 ['P','y']

Una heurística es, para una porción de cero a n, pensar: "cero es el comienzo, comenzar desde el principio y tomar n elementos en una lista".

>>> p[5] # the last of six items, indexed from zero
 'n'
>>> p[0:5] # does NOT include the last item!
 ['P','y','t','h','o']
>>> p[0:6] # not p[0:5]!!!
 ['P','y','t','h','o','n']

Otra heurística es, "para cualquier segmento, reemplace el inicio por cero, aplique la heurística anterior para obtener el final de la lista, luego cuente el primer número de nuevo para cortar los elementos desde el principio"

>>> p[0:4] # Start at the beginning and count out 4 items
 ['P','y','t','h']
>>> p[1:4] # Take one item off the front
 ['y','t','h']
>>> p[2:4] # Take two items off the front
 ['t','h']
# etc.

La primera regla de asignación de división es que, dado que la división devuelve una lista, la asignación de división requiere una lista (u otro iterable):

>>> p[2:3]
 ['t']
>>> p[2:3] = ['T']
>>> p
 ['P','y','T','h','o','n']
>>> p[2:3] = 't'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only assign an iterable

La segunda regla de la asignación de divisiones, que también puede ver arriba, es que cualquier porción de la lista se devuelve mediante la indexación de divisiones, esa es la misma porción que se cambia mediante la asignación de divisiones:

>>> p[2:4]
 ['T','h']
>>> p[2:4] = ['t','r']
>>> p
 ['P','y','t','r','o','n']

La tercera regla de asignación de divisiones es que la lista asignada (iterable) no tiene que tener la misma longitud; la porción indexada simplemente se corta y se reemplaza en masa por lo que se le asigne:

>>> p = ['P','y','t','h','o','n'] # Start over
>>> p[2:4] = ['s','p','a','m']
>>> p
 ['P','y','s','p','a','m','o','n']

La parte más complicada para acostumbrarse es la asignación a sectores vacíos. Con la heurística 1 y 2 es fácil entender cómo indexar una porción vacía:

>>> p = ['P','y','t','h','o','n']
>>> p[0:4]
 ['P','y','t','h']
>>> p[1:4]
 ['y','t','h']
>>> p[2:4]
 ['t','h']
>>> p[3:4]
 ['h']
>>> p[4:4]
 []

Y luego, una vez que haya visto eso, la asignación de la porción vacía también tiene sentido:

>>> p = ['P','y','t','h','o','n']
>>> p[2:4] = ['x','y'] # Assigned list is same length as slice
>>> p
 ['P','y','x','y','o','n'] # Result is same length
>>> p = ['P','y','t','h','o','n']
>>> p[3:4] = ['x','y'] # Assigned list is longer than slice
>>> p
 ['P','y','t','x','y','o','n'] # The result is longer
>>> p = ['P','y','t','h','o','n']
>>> p[4:4] = ['x','y']
>>> p
 ['P','y','t','h','x','y','o','n'] # The result is longer still

Tenga en cuenta que, dado que no estamos cambiando el segundo número del segmento (4), los elementos insertados siempre se apilan frente a la 'o', incluso cuando estamos asignando el segmento vacío. Entonces, la posición para la asignación de rebanada vacía es la extensión lógica de las posiciones para las asignaciones de rebanada no vacía.

Retrocediendo un poco, ¿qué sucede cuando continúas con nuestra procesión de contar el comienzo del corte?

>>> p = ['P','y','t','h','o','n']
>>> p[0:4]
 ['P','y','t','h']
>>> p[1:4]
 ['y','t','h']
>>> p[2:4]
 ['t','h']
>>> p[3:4]
 ['h']
>>> p[4:4]
 []
>>> p[5:4]
 []
>>> p[6:4]
 []

Con rebanar, una vez que haya terminado, habrá terminado; No comienza a cortar hacia atrás. En Python no obtienes avances negativos a menos que los pidas explícitamente usando un número negativo.

>>> p[5:3:-1]
 ['n','o']

Hay algunas consecuencias extrañas a la regla "una vez que hayas terminado, hayas terminado":

>>> p[4:4]
 []
>>> p[5:4]
 []
>>> p[6:4]
 []
>>> p[6]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

De hecho, en comparación con la indexación, el corte en Python es extrañamente a prueba de errores:

>>> p[100:200]
 []
>>> p[int(2e99):int(1e99)]
 []

Esto puede ser útil a veces, pero también puede conducir a un comportamiento algo extraño:

>>> p
 ['P', 'y', 't', 'h', 'o', 'n']
>>> p[int(2e99):int(1e99)] = ['p','o','w','e','r']
>>> p
 ['P', 'y', 't', 'h', 'o', 'n', 'p', 'o', 'w', 'e', 'r']

Dependiendo de su aplicación, eso podría ... o no ... ¡ser lo que esperaba allí!


A continuación se muestra el texto de mi respuesta original. Ha sido útil para muchas personas, por lo que no quería eliminarlo.

>>> r=[1,2,3,4]
>>> r[1:1]
[]
>>> r[1:1]=[9,8]
>>> r
[1, 9, 8, 2, 3, 4]
>>> r[1:1]=['blah']
>>> r
[1, 'blah', 9, 8, 2, 3, 4]

Esto también puede aclarar la diferencia entre cortar e indexar.

David M. Perlman
fuente
246

Explicar la notación de corte de Python

En resumen, los dos puntos ( :) en la notación de subíndice ( subscriptable[subscriptarg]) marca la notación de corte - que tiene los argumentos opcionales, start, stop, step:

sliceable[start:stop:step]

El corte en Python es una forma computacionalmente rápida de acceder metódicamente a partes de sus datos. En mi opinión, para ser incluso un programador intermedio de Python, es un aspecto del lenguaje con el que es necesario estar familiarizado.

Definiciones importantes

Para empezar, definamos algunos términos:

inicio: el índice inicial de la porción, incluirá el elemento en este índice a menos que sea lo mismo que detener , por defecto es 0, es decir, el primer índice. Si es negativo, significa comenzar nelementos desde el final.

stop: el índice final de la división, no incluye el elemento en este índice, por defecto la longitud de la secuencia que se divide, es decir, hasta el final.

paso: la cantidad en la que aumenta el índice, por defecto es 1. Si es negativo, estás cortando el iterable al revés.

Cómo funciona la indexación

Puedes hacer cualquiera de estos números positivos o negativos. El significado de los números positivos es sencillo, pero para los números negativos, al igual que los índices en Python, cuentas hacia atrás desde el final para el inicio y el final , y para el paso , simplemente disminuyes tu índice. Este ejemplo es del tutorial de la documentación , pero lo he modificado ligeramente para indicar a qué elemento de una secuencia hace referencia cada índice:

 +---+---+---+---+---+---+
 | P | y | t | h | o | n |
 +---+---+---+---+---+---+
   0   1   2   3   4   5 
  -6  -5  -4  -3  -2  -1

Cómo funciona el corte

Para usar la notación de corte con una secuencia que lo admita, debe incluir al menos dos puntos en los corchetes que siguen a la secuencia (que en realidad implementan el __getitem__método de la secuencia, de acuerdo con el modelo de datos de Python ).

La notación de corte funciona así:

sequence[start:stop:step]

Y recuerde que hay valores predeterminados para inicio , detención y paso , por lo que para acceder a los valores predeterminados, simplemente omita el argumento.

La notación de corte para obtener los últimos nueve elementos de una lista (o cualquier otra secuencia que lo soporte, como una cadena) se vería así:

my_list[-9:]

Cuando veo esto, leo la parte entre paréntesis como "noveno desde el final hasta el final". (En realidad, lo abrevio mentalmente como "-9, en")

Explicación:

La notación completa es

my_list[-9:None:None]

y para sustituir los valores predeterminados (en realidad, cuando stepes negativo, stopel valor predeterminado es -len(my_list) - 1, Nonepor lo que para detener realmente solo significa que va a cualquier paso final que lo lleve):

my_list[-9:len(my_list):1]

El colon , :es lo que le dice a Python que le está dando una porción y no un índice regular. Es por eso que la forma idiomática de hacer una copia superficial de listas en Python 2 es

list_copy = sequence[:]

Y limpiarlos es con:

del my_list[:]

(Python 3 obtiene un método list.copyy list.clear).

Cuando stepes negativo, los valores predeterminados starty stopcambian

Por defecto, cuando el stepargumento está vacío (o None), se le asigna +1.

Pero puede pasar un número entero negativo, y la lista (o la mayoría de los otros slicables estándar) se dividirá desde el final hasta el principio.

Por lo tanto, un segmento negativo cambiará los valores predeterminados para starty stop!

Confirmando esto en la fuente

Me gusta animar a los usuarios a leer la fuente y la documentación. El código fuente para los objetos de corte y esta lógica se encuentra aquí . Primero determinamos si stepes negativo:

 step_is_negative = step_sign < 0;

Si es así, el límite inferior -1 significa que dividimos todo el camino hasta el principio e incluido, y el límite superior es la longitud menos 1, lo que significa que comenzamos al final. (Tenga en cuenta que la semántica de esto -1es diferente de la -1que los usuarios pueden pasar índices en Python que indican el último elemento).

if (step_is_negative) {
    lower = PyLong_FromLong(-1L);
    if (lower == NULL)
        goto error;

    upper = PyNumber_Add(length, lower);
    if (upper == NULL)
        goto error;
}

De steplo contrario, es positivo, y el límite inferior será cero y el límite superior (que subiremos pero no incluiremos) la longitud de la lista dividida.

else {
    lower = _PyLong_Zero;
    Py_INCREF(lower);
    upper = length;
    Py_INCREF(upper);
}

Entonces, es posible que necesitemos aplicar los valores predeterminados para starty stop- el valor predeterminado para entonces startse calcula como el límite superior cuando stepes negativo:

if (self->start == Py_None) {
    start = step_is_negative ? upper : lower;
    Py_INCREF(start);
}

y stop, el límite inferior:

if (self->stop == Py_None) {
    stop = step_is_negative ? lower : upper;
    Py_INCREF(stop);
}

¡Dale a tus rebanadas un nombre descriptivo!

Puede resultarle útil separar la formación de la porción de pasarla al list.__getitem__método ( eso es lo que hacen los corchetes ). Incluso si no es nuevo, mantiene su código más legible para que otros que tengan que leer su código puedan comprender más fácilmente lo que está haciendo.

Sin embargo, no puede simplemente asignar algunos enteros separados por dos puntos a una variable. Necesita usar el objeto de corte:

last_nine_slice = slice(-9, None)

Se Nonerequiere el segundo argumento, para que el primer argumento se interprete como el startargumento, de lo contrario sería el stopargumento .

Luego puede pasar el objeto de corte a su secuencia:

>>> list(range(100))[last_nine_slice]
[91, 92, 93, 94, 95, 96, 97, 98, 99]

Es interesante que los rangos también tomen porciones:

>>> range(100)[last_nine_slice]
range(91, 100)

Consideraciones de memoria:

Dado que las secciones de las listas de Python crean nuevos objetos en la memoria, otra función importante a tener en cuenta es itertools.islice. Por lo general, querrá iterar sobre un segmento, no solo crearlo estáticamente en la memoria. islicees perfecto para esto Una advertencia, no admite argumentos negativos para start, stopo step, por lo que si ese es un problema, es posible que deba calcular índices o revertir el iterable de antemano.

length = 100
last_nine_iter = itertools.islice(list(range(length)), length-9, None, 1)
list_last_nine = list(last_nine_iter)

y ahora:

>>> list_last_nine
[91, 92, 93, 94, 95, 96, 97, 98, 99]

El hecho de que las secciones de la lista hagan una copia es una característica de las listas mismas. Si está cortando objetos avanzados como un Pandas DataFrame, puede devolver una vista del original y no una copia.

Aaron Hall
fuente
147

Y un par de cosas que no eran obvias para mí cuando vi por primera vez la sintaxis de corte:

>>> x = [1,2,3,4,5,6]
>>> x[::-1]
[6,5,4,3,2,1]

¡Manera fácil de invertir secuencias!

Y si desea, por alguna razón, cada segundo elemento en la secuencia inversa:

>>> x = [1,2,3,4,5,6]
>>> x[::-2]
[6,4,2]
Dana
fuente
100

En Python 2.7

Cortando en Python

[a:b:c]

len = length of string, tuple or list

c -- default is +1. The sign of c indicates forward or backward, absolute value of c indicates steps. Default is forward with step size 1. Positive means forward, negative means backward.

a --  When c is positive or blank, default is 0. When c is negative, default is -1.

b --  When c is positive or blank, default is len. When c is negative, default is -(len+1).

Comprender la asignación de índice es muy importante.

In forward direction, starts at 0 and ends at len-1

In backward direction, starts at -1 and ends at -len

Cuando dices [a: b: c], estás diciendo dependiendo del signo de c (hacia adelante o hacia atrás), comienza en a y termina en b (excluyendo el elemento en el índice bth). Use la regla de indexación anterior y recuerde que solo encontrará elementos en este rango:

-len, -len+1, -len+2, ..., 0, 1, 2,3,4 , len -1

Pero este rango continúa en ambas direcciones infinitamente:

...,-len -2 ,-len-1,-len, -len+1, -len+2, ..., 0, 1, 2,3,4 , len -1, len, len +1, len+2 , ....

Por ejemplo:

             0    1    2   3    4   5   6   7   8   9   10   11
             a    s    t   r    i   n   g
    -9  -8  -7   -6   -5  -4   -3  -2  -1

Si su elección de a, byc permite la superposición con el rango anterior a medida que recorre usando las reglas para a, b, c arriba, obtendrá una lista con elementos (tocada durante el recorrido) o obtendrá una lista vacía.

Una última cosa: si ayb son iguales, entonces también obtienes una lista vacía:

>>> l1
[2, 3, 4]

>>> l1[:]
[2, 3, 4]

>>> l1[::-1] # a default is -1 , b default is -(len+1)
[4, 3, 2]

>>> l1[:-4:-1] # a default is -1
[4, 3, 2]

>>> l1[:-3:-1] # a default is -1
[4, 3]

>>> l1[::] # c default is +1, so a default is 0, b default is len
[2, 3, 4]

>>> l1[::-1] # c is -1 , so a default is -1 and b default is -(len+1)
[4, 3, 2]


>>> l1[-100:-200:-1] # Interesting
[]

>>> l1[-1:-200:-1] # Interesting
[4, 3, 2]


>>> l1[-1:-1:1]
[]


>>> l1[-1:5:1] # Interesting
[4]


>>> l1[1:-7:1]
[]

>>> l1[1:-7:-1] # Interesting
[3, 2]

>>> l1[:-2:-2] # a default is -1, stop(b) at -2 , step(c) by 2 in reverse direction
[4]
Ankur Agarwal
fuente
2
otro ejemplo interesante: a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]; a[:-2:-2]que resulta a[9]
Deviacium
96

Encontré esta gran tabla en http://wiki.python.org/moin/MovingToPythonFromOtherLanguages

Python indexes and slices for a six-element list.
Indexes enumerate the elements, slices enumerate the spaces between the elements.

Index from rear:    -6  -5  -4  -3  -2  -1      a=[0,1,2,3,4,5]    a[1:]==[1,2,3,4,5]
Index from front:    0   1   2   3   4   5      len(a)==6          a[:5]==[0,1,2,3,4]
                   +---+---+---+---+---+---+    a[0]==0            a[:-2]==[0,1,2,3]
                   | a | b | c | d | e | f |    a[5]==5            a[1:2]==[1]
                   +---+---+---+---+---+---+    a[-1]==5           a[1:-1]==[1,2,3,4]
Slice from front:  :   1   2   3   4   5   :    a[-2]==4
Slice from rear:   :  -5  -4  -3  -2  -1   :
                                                b=a[:]
                                                b==[0,1,2,3,4,5] (shallow copy of a)
AdrianoFerrari
fuente
65

Después de usarlo un poco, me doy cuenta de que la descripción más simple es que es exactamente lo mismo que los argumentos en un forbucle ...

(from:to:step)

Cualquiera de ellos es opcional:

(:to:step)
(from::step)
(from:to)

Luego, la indexación negativa solo necesita que agregue la longitud de la cadena a los índices negativos para comprenderla.

Esto me funciona de todos modos ...

Simón
fuente
52

Me resulta más fácil recordar cómo funciona, y luego puedo descubrir cualquier combinación específica de inicio / parada / paso.

Es instructivo entender range()primero:

def range(start=0, stop, step=1):  # Illegal syntax, but that's the effect
    i = start
    while (i < stop if step > 0 else i > stop):
        yield i
        i += step

Comience desde start, incremente por step, no alcance stop. Muy simple.

Lo que hay que recordar sobre el paso negativo es que stopsiempre es el final excluido, ya sea más alto o más bajo. Si desea el mismo corte en orden opuesto, es mucho más limpio hacer la inversión por separado: por ejemplo, 'abcde'[1:-2][::-1]corta un carbón desde la izquierda, dos desde la derecha y luego invierte. (Ver también reversed())

El corte de secuencia es el mismo, excepto que primero normaliza los índices negativos y nunca puede salir de la secuencia:

TODO : El siguiente código tenía un error con "nunca salir de la secuencia" cuando abs (paso)> 1; Creo que lo parcheé para que sea correcto, pero es difícil de entender.

def this_is_how_slicing_works(seq, start=None, stop=None, step=1):
    if start is None:
        start = (0 if step > 0 else len(seq)-1)
    elif start < 0:
        start += len(seq)
    if not 0 <= start < len(seq):  # clip if still outside bounds
        start = (0 if step > 0 else len(seq)-1)
    if stop is None:
        stop = (len(seq) if step > 0 else -1)  # really -1, not last element
    elif stop < 0:
        stop += len(seq)
    for i in range(start, stop, step):
        if 0 <= i < len(seq):
            yield seq[i]

No se preocupe por los is Nonedetalles, solo recuerde que omitir starty / o stopsiempre hace lo correcto para darle toda la secuencia.

La normalización de los índices negativos primero permite que el inicio y / o la detención se cuenten desde el final de forma independiente: a 'abcde'[1:-2] == 'abcde'[1:3] == 'bc'pesar de range(1,-2) == []. La normalización a veces se considera como "módulo de la longitud", pero tenga en cuenta que agrega la longitud solo una vez: por ejemplo, 'abcde'[-53:42]es solo la cadena completa.

Beni Cherniavsky-Paskin
fuente
3
El this_is_how_slicing_worksno es lo mismo que el segmento de python. EG [0, 1, 2][-5:3:3]obtendrá [0] en python, pero list(this_is_how_slicing_works([0, 1, 2], -5, 3, 3))obtendrá [1].
Eastsun
@Eastsun ¡Vaya, tienes razón! Un caso más claro: range(4)[-200:200:3] == [0, 3]pero list(this_is_how_slicing_works([0, 1, 2, 3], -200, 200, 3)) == [2]. Mi if 0 <= i < len(seq):intento de implementar "nunca salga de la secuencia" simplemente pero está mal para el paso> 1. Lo reescribiré más tarde hoy (con pruebas).
Beni Cherniavsky-Paskin
40

Utilizo el método de "puntos de índice entre elementos" para pensarlo yo mismo, pero una forma de describirlo que a veces ayuda a otros a entenderlo es esta:

mylist[X:Y]

X es el índice del primer elemento que desea.
Y es el índice del primer elemento que no desea.

Steve Losh
fuente
40
Index:
      ------------>
  0   1   2   3   4
+---+---+---+---+---+
| a | b | c | d | e |
+---+---+---+---+---+
  0  -4  -3  -2  -1
      <------------

Slice:
    <---------------|
|--------------->
:   1   2   3   4   :
+---+---+---+---+---+
| a | b | c | d | e |
+---+---+---+---+---+
:  -4  -3  -2  -1   :
|--------------->
    <---------------|

Espero que esto te ayude a modelar la lista en Python.

Referencia: http://wiki.python.org/moin/MovingToPythonFromOtherLanguages

xiaoyu
fuente
38

Notación de corte de Python:

a[start:end:step]
  • Para starty end, los valores negativos se interpretan como relativos al final de la secuencia.
  • Los índices positivos para endindican la posición después del último elemento que se incluirá.
  • Los valores del blanco están predeterminadas como sigue: [+0:-0:1].
  • El uso de un paso negativo revierte la interpretación de startyend

La notación se extiende a matrices (numpy) y matrices multidimensionales. Por ejemplo, para cortar columnas enteras puede usar:

m[::,0:2:] ## slice the first two columns

Los sectores contienen referencias, no copias, de los elementos de la matriz. Si desea hacer una copia separada de una matriz, puede usarla deepcopy().

sin bar
fuente
34

También puede usar la asignación de divisiones para eliminar uno o más elementos de una lista:

r = [1, 'blah', 9, 8, 2, 3, 4]
>>> r[1:4] = []
>>> r
[1, 2, 3, 4]
dansalmo
fuente
33

Esto es solo para obtener información adicional ... Considere la lista a continuación

>>> l=[12,23,345,456,67,7,945,467]

Algunos otros trucos para revertir la lista:

>>> l[len(l):-len(l)-1:-1]
[467, 945, 7, 67, 456, 345, 23, 12]

>>> l[:-len(l)-1:-1]
[467, 945, 7, 67, 456, 345, 23, 12]

>>> l[len(l)::-1]
[467, 945, 7, 67, 456, 345, 23, 12]

>>> l[::-1]
[467, 945, 7, 67, 456, 345, 23, 12]

>>> l[-1:-len(l)-1:-1]
[467, 945, 7, 67, 456, 345, 23, 12]
Arindam Roychowdhury
fuente
33

Así es como enseño rebanadas a los novatos:

Comprender la diferencia entre indexación y segmentación:

Wiki Python tiene esta increíble imagen que distingue claramente la indexación y el corte.

Ingrese la descripción de la imagen aquí

Es una lista con seis elementos. Para entender mejor el corte, considere esa lista como un conjunto de seis cajas juntas. Cada caja tiene un alfabeto en ella.

La indexación es como tratar con el contenido de la caja. Puede verificar el contenido de cualquier casilla. Pero no puede verificar el contenido de varias cajas a la vez. Incluso puede reemplazar el contenido de la caja. Pero no puede colocar dos bolas en una caja o reemplazar dos bolas a la vez.

In [122]: alpha = ['a', 'b', 'c', 'd', 'e', 'f']

In [123]: alpha
Out[123]: ['a', 'b', 'c', 'd', 'e', 'f']

In [124]: alpha[0]
Out[124]: 'a'

In [127]: alpha[0] = 'A'

In [128]: alpha
Out[128]: ['A', 'b', 'c', 'd', 'e', 'f']

In [129]: alpha[0,1]
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-129-c7eb16585371> in <module>()
----> 1 alpha[0,1]

TypeError: list indices must be integers, not tuple

Rebanar es como tratar con cajas en sí. Puede levantar la primera caja y colocarla en otra mesa. Para recoger el cuadro, todo lo que necesita saber es la posición de inicio y finalización del cuadro.

Incluso puede recoger los primeros tres cuadros o los dos últimos cuadros o todos los cuadros entre 1 y 4. Por lo tanto, puede elegir cualquier conjunto de cuadros si conoce el principio y el final. Estas posiciones se denominan posiciones de inicio y parada.

Lo interesante es que puede reemplazar varias cajas a la vez. También puedes colocar múltiples cajas donde quieras.

In [130]: alpha[0:1]
Out[130]: ['A']

In [131]: alpha[0:1] = 'a'

In [132]: alpha
Out[132]: ['a', 'b', 'c', 'd', 'e', 'f']

In [133]: alpha[0:2] = ['A', 'B']

In [134]: alpha
Out[134]: ['A', 'B', 'c', 'd', 'e', 'f']

In [135]: alpha[2:2] = ['x', 'xx']

In [136]: alpha
Out[136]: ['A', 'B', 'x', 'xx', 'c', 'd', 'e', 'f']

Cortando con paso:

Hasta ahora has elegido cajas continuamente. Pero a veces necesitas recoger discretamente. Por ejemplo, puede recoger cada segundo cuadro. Incluso puede recoger cada tercer cuadro desde el final. Este valor se llama tamaño de paso. Esto representa la brecha entre sus recogidas sucesivas. El tamaño del paso debe ser positivo si está seleccionando cuadros de principio a fin y viceversa.

In [137]: alpha = ['a', 'b', 'c', 'd', 'e', 'f']

In [142]: alpha[1:5:2]
Out[142]: ['b', 'd']

In [143]: alpha[-1:-5:-2]
Out[143]: ['f', 'd']

In [144]: alpha[1:5:-2]
Out[144]: []

In [145]: alpha[-1:-5:2]
Out[145]: []

Cómo Python descubre los parámetros perdidos:

Al dividir, si omite algún parámetro, Python intenta resolverlo automáticamente.

Si verifica el código fuente de CPython , encontrará una función llamada PySlice_GetIndicesEx () que calcula los índices de un segmento para cualquier parámetro dado. Aquí está el código equivalente lógico en Python.

Esta función toma un objeto Python y parámetros opcionales para segmentar y devuelve el inicio, la detención, el paso y la longitud del segmento para el segmento solicitado.

def py_slice_get_indices_ex(obj, start=None, stop=None, step=None):

    length = len(obj)

    if step is None:
        step = 1
    if step == 0:
        raise Exception("Step cannot be zero.")

    if start is None:
        start = 0 if step > 0 else length - 1
    else:
        if start < 0:
            start += length
        if start < 0:
            start = 0 if step > 0 else -1
        if start >= length:
            start = length if step > 0 else length - 1

    if stop is None:
        stop = length if step > 0 else -1
    else:
        if stop < 0:
            stop += length
        if stop < 0:
            stop = 0 if step > 0 else -1
        if stop >= length:
            stop = length if step > 0 else length - 1

    if (step < 0 and stop >= start) or (step > 0 and start >= stop):
        slice_length = 0
    elif step < 0:
        slice_length = (stop - start + 1)/(step) + 1
    else:
        slice_length = (stop - start - 1)/(step) + 1

    return (start, stop, step, slice_length)

Esta es la inteligencia que está presente detrás de las rebanadas. Dado que Python tiene una función incorporada llamada slice, puede pasar algunos parámetros y verificar qué tan inteligentemente calcula los parámetros faltantes.

In [21]: alpha = ['a', 'b', 'c', 'd', 'e', 'f']

In [22]: s = slice(None, None, None)

In [23]: s
Out[23]: slice(None, None, None)

In [24]: s.indices(len(alpha))
Out[24]: (0, 6, 1)

In [25]: range(*s.indices(len(alpha)))
Out[25]: [0, 1, 2, 3, 4, 5]

In [26]: s = slice(None, None, -1)

In [27]: range(*s.indices(len(alpha)))
Out[27]: [5, 4, 3, 2, 1, 0]

In [28]: s = slice(None, 3, -1)

In [29]: range(*s.indices(len(alpha)))
Out[29]: [5, 4]

Nota: Esta publicación se escribió originalmente en mi blog, The Intelligence Behind Python Slices .

ChillarAnand
fuente
29

Como regla general, escribir código con muchos valores de índice codificados conduce a un lío de legibilidad y mantenimiento. Por ejemplo, si vuelve al código un año después, lo mirará y se preguntará qué estaba pensando cuando lo escribió. La solución que se muestra es simplemente una forma de establecer más claramente lo que su código está haciendo realmente. En general, el sector integrado () crea un objeto de sector que se puede utilizar en cualquier lugar donde se permita un sector. Por ejemplo:

>>> items = [0, 1, 2, 3, 4, 5, 6]
>>> a = slice(2, 4)
>>> items[2:4]
[2, 3]
>>> items[a]
[2, 3]
>>> items[a] = [10,11]
>>> items
[0, 1, 10, 11, 4, 5, 6]
>>> del items[a]
>>> items
[0, 1, 4, 5, 6]

Si tiene una instancia de segmento s, puede obtener más información al mirar sus atributos s.start, s.stop y s.step, respectivamente. Por ejemplo:

>>> a = slice(10, 50, 2)
>>> a.start
10
>>> a.stop
50
>>> a.step
2
>>>
Python_Dude
fuente
25

1. Notación de corte

Para simplificar, recuerde que la rebanada solo tiene una forma:

s[start:end:step]

y así es como funciona:

  • s: un objeto que puede ser cortado
  • start: primer índice para iniciar la iteración
  • end: último índice, TEN EN CUENTA que el endíndice no se incluirá en el segmento resultante
  • step: elija elemento cada stepíndice

Otra cosa importación: todos start, end, stepse puede omitir! Y si se omiten, su valor por defecto será usada: 0, len(s), 1en consecuencia.

Entonces las posibles variaciones son:

# Mostly used variations
s[start:end]
s[start:]
s[:end]

# Step-related variations
s[:end:step]
s[start::step]
s[::step]

# Make a copy
s[:]

NOTA: Si start >= end(considerando solo cuándo step>0), Python devolverá un segmento vacío [].

2. Errores

La parte anterior explica las características principales sobre cómo funciona el corte, y funcionará en la mayoría de las ocasiones. Sin embargo, puede haber dificultades que debe tener cuidado, y esta parte las explica.

Índices negativos

¡Lo primero que confunde a los estudiantes de Python es que un índice puede ser negativo! No entre en pánico: un índice negativo significa contar hacia atrás.

Por ejemplo:

s[-5:]    # Start at the 5th index from the end of array,
          # thus returning the last 5 elements.
s[:-5]    # Start at index 0, and end until the 5th index from end of array,
          # thus returning s[0:len(s)-5].

Paso negativo

¡Hacer las cosas más confusas también steppuede ser negativo!

Un paso negativo significa iterar la matriz hacia atrás: desde el final hasta el inicio, con el índice final incluido y el índice inicial excluido del resultado.

NOTA : cuando el paso es negativo, el valor predeterminado para startes len(s)(mientras que endno es igual a 0, porque s[::-1]contiene s[0]). Por ejemplo:

s[::-1]            # Reversed slice
s[len(s)::-1]      # The same as above, reversed slice
s[0:len(s):-1]     # Empty list

Error fuera de rango?

Sorpréndase : ¡el sector no genera un IndexError cuando el índice está fuera de rango!

Si el índice está fuera de rango, Python hará todo lo posible para establecer el índice 0o len(s)según la situación. Por ejemplo:

s[:len(s)+5]      # The same as s[:len(s)]
s[-len(s)-5::]    # The same as s[0:]
s[len(s)+5::-1]   # The same as s[len(s)::-1], and the same as s[::-1]

3. Ejemplos

Terminemos esta respuesta con ejemplos, explicando todo lo que hemos discutido:

# Create our array for demonstration
In [1]: s = [i for i in range(10)]

In [2]: s
Out[2]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [3]: s[2:]   # From index 2 to last index
Out[3]: [2, 3, 4, 5, 6, 7, 8, 9]

In [4]: s[:8]   # From index 0 up to index 8
Out[4]: [0, 1, 2, 3, 4, 5, 6, 7]

In [5]: s[4:7]  # From index 4 (included) up to index 7(excluded)
Out[5]: [4, 5, 6]

In [6]: s[:-2]  # Up to second last index (negative index)
Out[6]: [0, 1, 2, 3, 4, 5, 6, 7]

In [7]: s[-2:]  # From second last index (negative index)
Out[7]: [8, 9]

In [8]: s[::-1] # From last to first in reverse order (negative step)
Out[8]: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

In [9]: s[::-2] # All odd numbers in reversed order
Out[9]: [9, 7, 5, 3, 1]

In [11]: s[-2::-2] # All even numbers in reversed order
Out[11]: [8, 6, 4, 2, 0]

In [12]: s[3:15]   # End is out of range, and Python will set it to len(s).
Out[12]: [3, 4, 5, 6, 7, 8, 9]

In [14]: s[5:1]    # Start > end; return empty list
Out[14]: []

In [15]: s[11]     # Access index 11 (greater than len(s)) will raise an IndexError
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-15-79ffc22473a3> in <module>()
----> 1 s[11]

IndexError: list index out of range
cizixs
fuente
24

Las respuestas anteriores no discuten el corte de matriz multidimensional que es posible usando el famoso paquete NumPy :

La división también se puede aplicar a matrices multidimensionales.

# Here, a is a NumPy array

>>> a
array([[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12]])
>>> a[:2, 0:3:2]
array([[1, 3],
       [5, 7]])

El " :2" antes de que la coma opere en la primera dimensión y el " 0:3:2" después de que la coma opere en la segunda dimensión.

Statham
fuente
44
Solo un recordatorio amistoso de que no puedes hacer esto en Python listsino solo arrayen Numpy
Mars Lee
15
#!/usr/bin/env python

def slicegraphical(s, lista):

    if len(s) > 9:
        print """Enter a string of maximum 9 characters,
    so the printig would looki nice"""
        return 0;
    # print " ",
    print '  '+'+---' * len(s) +'+'
    print ' ',
    for letter in s:
        print '| {}'.format(letter),
    print '|'
    print " ",; print '+---' * len(s) +'+'

    print " ",
    for letter in range(len(s) +1):
        print '{}  '.format(letter),
    print ""
    for letter in range(-1*(len(s)), 0):
        print ' {}'.format(letter),
    print ''
    print ''


    for triada in lista:
        if len(triada) == 3:
            if triada[0]==None and triada[1] == None and triada[2] == None:
                # 000
                print s+'[   :   :   ]' +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] == None and triada[1] == None and triada[2] != None:
                # 001
                print s+'[   :   :{0:2d} ]'.format(triada[2], '','') +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] == None and triada[1] != None and triada[2] == None:
                # 010
                print s+'[   :{0:2d} :   ]'.format(triada[1]) +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] == None and triada[1] != None and triada[2] != None:
                # 011
                print s+'[   :{0:2d} :{1:2d} ]'.format(triada[1], triada[2]) +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] != None and triada[1] == None and triada[2] == None:
                # 100
                print s+'[{0:2d} :   :   ]'.format(triada[0]) +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] != None and triada[1] == None and triada[2] != None:
                # 101
                print s+'[{0:2d} :   :{1:2d} ]'.format(triada[0], triada[2]) +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] != None and triada[1] != None and triada[2] == None:
                # 110
                print s+'[{0:2d} :{1:2d} :   ]'.format(triada[0], triada[1]) +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] != None and triada[1] != None and triada[2] != None:
                # 111
                print s+'[{0:2d} :{1:2d} :{2:2d} ]'.format(triada[0], triada[1], triada[2]) +' = ', s[triada[0]:triada[1]:triada[2]]

        elif len(triada) == 2:
            if triada[0] == None and triada[1] == None:
                # 00
                print s+'[   :   ]    ' + ' = ', s[triada[0]:triada[1]]
            elif triada[0] == None and triada[1] != None:
                # 01
                print s+'[   :{0:2d} ]    '.format(triada[1]) + ' = ', s[triada[0]:triada[1]]
            elif triada[0] != None and triada[1] == None:
                # 10
                print s+'[{0:2d} :   ]    '.format(triada[0]) + ' = ', s[triada[0]:triada[1]]
            elif triada[0] != None and triada[1] != None:
                # 11
                print s+'[{0:2d} :{1:2d} ]    '.format(triada[0],triada[1]) + ' = ', s[triada[0]:triada[1]]

        elif len(triada) == 1:
            print s+'[{0:2d} ]        '.format(triada[0]) + ' = ', s[triada[0]]


if __name__ == '__main__':
    # Change "s" to what ever string you like, make it 9 characters for
    # better representation.
    s = 'COMPUTERS'

    # add to this list different lists to experement with indexes
    # to represent ex. s[::], use s[None, None,None], otherwise you get an error
    # for s[2:] use s[2:None]

    lista = [[4,7],[2,5,2],[-5,1,-1],[4],[-4,-6,-1], [2,-3,1],[2,-3,-1], [None,None,-1],[-5,None],[-5,0,-1],[-5,None,-1],[-1,1,-2]]

    slicegraphical(s, lista)

Puede ejecutar este script y experimentar con él, a continuación hay algunas muestras que obtuve del script.

  +---+---+---+---+---+---+---+---+---+
  | C | O | M | P | U | T | E | R | S |
  +---+---+---+---+---+---+---+---+---+
  0   1   2   3   4   5   6   7   8   9   
 -9  -8  -7  -6  -5  -4  -3  -2  -1 

COMPUTERS[ 4 : 7 ]     =  UTE
COMPUTERS[ 2 : 5 : 2 ] =  MU
COMPUTERS[-5 : 1 :-1 ] =  UPM
COMPUTERS[ 4 ]         =  U
COMPUTERS[-4 :-6 :-1 ] =  TU
COMPUTERS[ 2 :-3 : 1 ] =  MPUT
COMPUTERS[ 2 :-3 :-1 ] =  
COMPUTERS[   :   :-1 ] =  SRETUPMOC
COMPUTERS[-5 :   ]     =  UTERS
COMPUTERS[-5 : 0 :-1 ] =  UPMO
COMPUTERS[-5 :   :-1 ] =  UPMOC
COMPUTERS[-1 : 1 :-2 ] =  SEUM
[Finished in 0.9s]

Cuando utilice un paso negativo, observe que la respuesta se desplaza a la derecha por 1.

mahmoh
fuente
14

Mi cerebro parece feliz de aceptar que lst[start:end]contiene el startenésimo elemento. Incluso podría decir que es una 'suposición natural'.

Pero ocasionalmente aparece una duda y mi cerebro me pide que me asegure que no contiene el endelemento -th.

En estos momentos confío en este simple teorema:

for any n,    lst = lst[:n] + lst[n:]

Esta bonita propiedad me dice que lst[start:end]no contiene el endelemento -th porque está en lst[end:].

Tenga en cuenta que este teorema es verdadero para cualquiera n. Por ejemplo, puedes verificar que

lst = range(10)
lst[:-42] + lst[-42:] == lst

vuelve True.

Robert
fuente
12

En mi opinión, comprenderá y memorizará mejor la notación de corte de cadena de Python si la mira de la siguiente manera (siga leyendo).

Trabajemos con la siguiente cadena ...

azString = "abcdefghijklmnopqrstuvwxyz"

Para aquellos que no saben, puede crear cualquier subcadena azStringutilizando la notaciónazString[x:y]

Viniendo de otros lenguajes de programación, es cuando el sentido común se ve comprometido. ¿Qué son x e y?

Tuve que sentarme y ejecutar varios escenarios en mi búsqueda de una técnica de memorización que me ayudará a recordar qué son x e y y me ayudará a cortar las cadenas correctamente en el primer intento.

Mi conclusión es que x e y deben verse como los índices de límite que rodean las cadenas que queremos agregar. Entonces deberíamos ver la expresión como azString[index1, index2]o incluso más clara como azString[index_of_first_character, index_after_the_last_character].

Aquí hay un ejemplo de visualización de eso ...

Letters   a b c d e f g h i j ...
         ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
             ┊           ┊
Indexes  0 1 2 3 4 5 6 7 8 9 ...
             ┊           ┊
cdefgh    index1       index2

Entonces, todo lo que tiene que hacer es establecer index1 e index2 a los valores que rodearán la subcadena deseada. Por ejemplo, para obtener la subcadena "cdefgh", puede usar azString[2:8], porque el índice en el lado izquierdo de "c" es 2 y el del tamaño correcto de "h" es 8.

Recuerda que estamos estableciendo los límites. Y esos límites son las posiciones donde podría colocar algunos soportes que se envolverán alrededor de la subcadena de esta manera ...

ab [ cdefgh ] ij

Ese truco funciona todo el tiempo y es fácil de memorizar.

asiby
fuente
11

La mayoría de las respuestas anteriores aclara las preguntas sobre la notación de corte.

La sintaxis de indexación extendida utilizada para segmentar es aList[start:stop:step], y los ejemplos básicos son:

Ingrese la descripción de la imagen aquí:

Más ejemplos de corte: 15 rebanadas extendidas

Roshan Bagdiya
fuente
10

En Python, la forma más básica para cortar es la siguiente:

l[start:end]

donde lhay alguna colección, startes un índice inclusivo y endes un índice exclusivo.

In [1]: l = list(range(10))

In [2]: l[:5] # First five elements
Out[2]: [0, 1, 2, 3, 4]

In [3]: l[-5:] # Last five elements
Out[3]: [5, 6, 7, 8, 9]

Al dividir desde el principio, puede omitir el índice cero, y al dividir hasta el final, puede omitir el índice final ya que es redundante, así que no sea detallado:

In [5]: l[:3] == l[0:3]
Out[5]: True

In [6]: l[7:] == l[7:len(l)]
Out[6]: True

Los enteros negativos son útiles cuando se realizan desplazamientos relativos al final de una colección:

In [7]: l[:-1] # Include all elements but the last one
Out[7]: [0, 1, 2, 3, 4, 5, 6, 7, 8]

In [8]: l[-3:] # Take the last three elements
Out[8]: [7, 8, 9]

Es posible proporcionar índices que están fuera de los límites al cortar, tales como:

In [9]: l[:20] # 20 is out of index bounds, and l[20] will raise an IndexError exception
Out[9]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [11]: l[-20:] # -20 is out of index bounds, and l[-20] will raise an IndexError exception
Out[11]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Tenga en cuenta que el resultado de cortar una colección es una colección completamente nueva. Además, cuando se usa la notación de corte en las asignaciones, la longitud de las asignaciones de corte no necesita ser la misma. Los valores antes y después del segmento asignado se mantendrán, y la colección se reducirá o crecerá para contener los nuevos valores:

In [16]: l[2:6] = list('abc') # Assigning fewer elements than the ones contained in the sliced collection l[2:6]

In [17]: l
Out[17]: [0, 1, 'a', 'b', 'c', 6, 7, 8, 9]

In [18]: l[2:5] = list('hello') # Assigning more elements than the ones contained in the sliced collection l [2:5]

In [19]: l
Out[19]: [0, 1, 'h', 'e', 'l', 'l', 'o', 6, 7, 8, 9]

Si omite el índice de inicio y finalización, hará una copia de la colección:

In [14]: l_copy = l[:]

In [15]: l == l_copy and l is not l_copy
Out[15]: True

Si se omiten los índices de inicio y fin cuando se realiza una operación de asignación, todo el contenido de la colección se reemplazará con una copia de lo que se hace referencia:

In [20]: l[:] = list('hello...')

In [21]: l
Out[21]: ['h', 'e', 'l', 'l', 'o', '.', '.', '.']

Además del corte básico, también es posible aplicar la siguiente notación:

l[start:end:step]

donde les una colección, startes un índice inclusivo, endes un índice exclusivo y stepes un paso que se puede utilizar para incorporar cada enésimo elemento l.

In [22]: l = list(range(10))

In [23]: l[::2] # Take the elements which indexes are even
Out[23]: [0, 2, 4, 6, 8]

In [24]: l[1::2] # Take the elements which indexes are odd
Out[24]: [1, 3, 5, 7, 9]

Usar stepproporciona un truco útil para revertir una colección en Python:

In [25]: l[::-1]
Out[25]: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

También es posible usar enteros negativos para stepel siguiente ejemplo:

In[28]:  l[::-2]
Out[28]: [9, 7, 5, 3, 1]

Sin embargo, usar un valor negativo para steppodría ser muy confuso. Por otra parte, con el fin de ser Pythonic , debe evitarse el uso start, endy stepen una sola rebanada. En caso de que sea necesario, considere hacer esto en dos tareas (una para cortar y la otra para avanzar).

In [29]: l = l[::2] # This step is for striding

In [30]: l
Out[30]: [0, 2, 4, 6, 8]

In [31]: l = l[1:-1] # This step is for slicing

In [32]: l
Out[32]: [2, 4, 6]
lmiguelvargasf
fuente
10

Quiero agregar uno ¡Hola Mundo! ejemplo que explica los conceptos básicos de las rebanadas para los principiantes. Me ayudó mucho.

Tengamos una lista con seis valores ['P', 'Y', 'T', 'H', 'O', 'N']:

+---+---+---+---+---+---+
| P | Y | T | H | O | N |
+---+---+---+---+---+---+
  0   1   2   3   4   5

Ahora los sectores más simples de esa lista son sus sublistas. La notación es [<index>:<index>]y la clave es leerlo así:

[ start cutting before this index : end cutting before this index ]

Ahora, si haces una porción [2:5]de la lista anterior, esto sucederá:

        |           |
+---+---|---+---+---|---+
| P | Y | T | H | O | N |
+---+---|---+---+---|---+
  0   1 | 2   3   4 | 5

Hiciste un corte antes del elemento con índice 2y otro corte antes del elemento con índice 5. Entonces, el resultado será un corte entre esos dos cortes, una lista ['T', 'H', 'O'].

Jeyekomon
fuente
10

Personalmente lo pienso como un forbucle:

a[start:end:step]
# for(i = start; i < end; i += step)

Además, tenga en cuenta que los valores negativos para starty endson relativos al final de la lista y se calculan en el ejemplo anterior por given_index + a.shape[0].

Raman
fuente
8

El siguiente es el ejemplo de un índice de una cadena:

 +---+---+---+---+---+
 | H | e | l | p | A |
 +---+---+---+---+---+
 0   1   2   3   4   5
-5  -4  -3  -2  -1

str="Name string"

Ejemplo de corte: [inicio: fin: paso]

str[start:end] # Items start through end-1
str[start:]    # Items start through the rest of the array
str[:end]      # Items from the beginning through end-1
str[:]         # A copy of the whole array

A continuación se muestra el uso de ejemplo:

print str[0] = N
print str[0:2] = Na
print str[0:7] = Name st
print str[0:7:2] = Nm t
print str[0:-1:2] = Nm ti
Príncipe Dhadwal
fuente
5

Si siente que los índices negativos en el corte es confuso, aquí hay una manera muy fácil de pensarlo: simplemente reemplace el índice negativo con len - index. Entonces, por ejemplo, reemplace -3 con len(list) - 3.

La mejor manera de ilustrar lo que hace el corte interno es simplemente mostrarlo en el código que implementa esta operación:

def slice(list, start = None, end = None, step = 1):
  # Take care of missing start/end parameters
  start = 0 if start is None else start
  end = len(list) if end is None else end

  # Take care of negative start/end parameters
  start = len(list) + start if start < 0 else start
  end = len(list) + end if end < 0 else end

  # Now just execute a for-loop with start, end and step
  return [list[i] for i in range(start, end, step)]
Shital Shah
fuente
4

La técnica básica de corte es definir el punto de partida, el punto de parada y el tamaño del paso, también conocido como zancada.

Primero, crearemos una lista de valores para usar en nuestro corte.

Crea dos listas para cortar. La primera es una lista numérica del 1 al 9 (Lista A). La segunda también es una lista numérica, de 0 a 9 (Lista B):

A = list(range(1, 10, 1)) # Start, stop, and step
B = list(range(9))

print("This is List A:", A)
print("This is List B:", B)

Indice el número 3 de A y el número 6 de B.

print(A[2])
print(B[6])

Rebanado Básico

La sintaxis de indexación extendida utilizada para segmentar es aList [start: stop: step]. El argumento de inicio y el argumento de paso están predeterminados en ninguno: el único argumento requerido es detener. ¿Notó que esto es similar a cómo se usó el rango para definir las listas A y B? Esto se debe a que el objeto de división representa el conjunto de índices especificados por rango (inicio, detención, paso). Documentación de Python 3.4.

Como puede ver, definir solo stop devuelve un elemento. Dado que el inicio por defecto es ninguno, esto se traduce en recuperar solo un elemento.

Es importante tener en cuenta que el primer elemento es el índice 0, no el índice 1. Es por eso que estamos utilizando 2 listas para este ejercicio. Los elementos de la Lista A están numerados de acuerdo con la posición ordinal (el primer elemento es 1, el segundo elemento es 2, etc.) mientras que los elementos de la Lista B son los números que se usarían para indexarlos ([0] para el primer elemento 0, etc.)

Con la sintaxis de indexación extendida, recuperamos un rango de valores. Por ejemplo, todos los valores se recuperan con dos puntos.

A[:]

Para recuperar un subconjunto de elementos, es necesario definir las posiciones de inicio y detención.

Dado el patrón aList [inicio: detener], recupere los dos primeros elementos de la Lista A.

Babu Chandermani
fuente
3

No creo que el diagrama del tutorial de Python (citado en varias otras respuestas) sea bueno ya que esta sugerencia funciona para un paso positivo, pero no para un paso negativo.

Este es el diagrama:

 +---+---+---+---+---+---+
 | P | y | t | h | o | n |
 +---+---+---+---+---+---+
 0   1   2   3   4   5   6
-6  -5  -4  -3  -2  -1

Desde el diagrama, espero a[-4,-6,-1]que sea, yPpero lo es ty.

>>> a = "Python"
>>> a[2:4:1] # as expected
'th'
>>> a[-4:-6:-1] # off by 1
'ty'

Lo que siempre funciona es pensar en caracteres o ranuras y usar la indexación como un intervalo medio abierto: abierto a la derecha si es un paso positivo, a la izquierda si es un paso negativo.

De esta manera, se me ocurre a[-4:-6:-1]que a(-6,-4]en la terminología de intervalo.

 +---+---+---+---+---+---+
 | P | y | t | h | o | n |
 +---+---+---+---+---+---+
   0   1   2   3   4   5  
  -6  -5  -4  -3  -2  -1

 +---+---+---+---+---+---+---+---+---+---+---+---+
 | P | y | t | h | o | n | P | y | t | h | o | n |
 +---+---+---+---+---+---+---+---+---+---+---+---+
  -6  -5  -4  -3  -2  -1   0   1   2   3   4   5  
aguadopd
fuente