Pregunta Comprender la notación de división de Python


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

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

Se ve extremadamente poderoso, pero no me he dado cuenta.


2290
2018-02-03 22:31


origen


Respuestas:


Es bastante simple realmente:

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

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

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

El punto clave para recordar es que el :end valor representa el primer valor que es no en el segmento seleccionado Entonces, la diferencia entre end y start es la cantidad de elementos seleccionados (si step es 1, el valor predeterminado).

La otra característica es que start o end tal vez un negativo número, lo que significa que cuenta desde el final de la matriz en lugar de desde el principio. Asi que:

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

Similar, step Puede 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 pide a[:-2] y a solo contiene un elemento, obtienes una lista vacía en lugar de un error. Algunas veces preferirías el error, así que debes ser consciente de que esto puede suceder.


3094
2018-02-03 22:48



los Tutorial de Python habla de ello (desplácese un poco hacia abajo hasta que llegue a la parte sobre cortar).

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

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

Una manera 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 secuencia de norte personajes tiene índice norte.


394
2018-02-03 22:49



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 menor que high-1.

Si stride es negativo, el orden ha cambiado un poco ya que estamos contando hacia abajo:

>>> 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]]

El corte extendido (con comas y elipses) se usa principalmente solo por estructuras de datos especiales (como Numpy); las secuencias básicas no los admiten.

>>> class slicee:
...     def __getitem__(self, item):
...         return `item`
...
>>> slicee()[0, 1:2, ::5, ...]
'(0, slice(1, 2, None), slice(None, None, 5), Ellipsis)'

310
2018-02-03 23:08



Las respuestas anteriores no discuten la asignación de sectores:

>>> 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.


199
2018-01-18 21:37



Explicar la notación de división de Python

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

sliceable[start:stop:step]

El corte de 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:

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

detener: el índice final de la porción, lo hace no incluir el elemento en este índice, por defecto es la longitud de la secuencia cortada, es decir, hasta e incluyendo el final.

paso: la cantidad por la cual el índice aumenta, se establece de manera predeterminada en 1. Si es negativo, está cortando el iterable en reversa.

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, se cuenta hacia atrás desde el final para el comienzo y detenery para el paso, simplemente disminuyes tu índice. Este ejemplo es del tutorial de la documentación, pero lo he modificado ligeramente para indicar qué elemento de una secuencia hace referencia a cada índice:

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

Cómo funciona Slicing

Para usar la notación de corte con una secuencia que lo admita, debe incluir al menos un punto y coma entre los corchetes que siguen la secuencia (que en realidad implementar el __getitem__ método de la secuencia, de acuerdo con el modelo de datos de Python.)

La notación de división funciona así:

sequence[start:stop:step]

Y recuerde que hay valores predeterminados para comienzo, detenery paso, para acceder a los valores predeterminados, simplemente omita el argumento.

La notación de división 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 corchetes como "novena desde el final hasta el final". (En realidad, lo abrevia mentalmente como "-9, encendido")

Explicación:

La notación completa es

my_list[-9:None:None]

y para sustituir los valores predeterminados (en realidad, cuando step es negativo, stopEl valor predeterminado es -len(my_list) - 1, asi que None para detener realmente solo significa que va al paso final que lo lleve):

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

los colon, :, es lo que le dice a Python que le está dando un corte 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 borrarlos es con:

del my_list[:]

(Python 3 obtiene una list.copy y list.clear método.)

Cuando step es negativo, los valores predeterminados para start y stop cambio

Por defecto, cuando el step el argumento está vacío (o None), está asignado a +1.

Pero puede pasar un entero negativo, y la lista (o la mayoría de las otras aplicaciones estándar) se dividirá desde el final hasta el principio.

Por lo tanto, un corte negativo cambiará los valores predeterminados para start y stop!

Confirmando esto en la fuente

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

 step_is_negative = step_sign < 0;

Si es así, el límite inferior es -1  lo que significa que dividimos todo el camino hasta e incluyendo el comienzo, 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 este -1 es diferente a partir de una -1 que 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 otra manera step es positivo, y el límite inferior será cero y el límite superior (que subiremos pero no incluirá) la longitud de la lista de sectores.

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

Entonces, podemos necesitar aplicar los valores predeterminados para start y stop - el valor predeterminado entonces para start se calcula como el límite superior cuando step es 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 ser útil separar la formación de la rebanada de pasarla a la list.__getitem__ método (eso es lo que hacen los corchetes) Incluso si no eres nuevo en esto, mantiene tu código más legible para que otros que puedan tener que leer tu código puedan entender más fácilmente lo que estás haciendo.

Sin embargo, no puedes asignar algunos enteros separados por dos puntos a una variable. Necesitas usar el objeto slice:

last_nine_slice = slice(-9, None)

El segundo argumento, None, se requiere, de modo que el primer argumento se interpreta como el start argumento de lo contrario, sería el stop argumento.

A continuación, 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 toman rodajas:

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

Consideraciones de memoria:

Como las porciones de listas de Python crean nuevos objetos en la memoria, otra función importante a tener en cuenta es itertools.islice. Normalmente, querrá iterar sobre un sector, no solo crearlo estáticamente en la memoria. islice es perfecto para esto Una advertencia, no admite argumentos negativos para start, stop, o step, por lo tanto, si ese es un problema, es posible que deba calcular índices o invertir el iterable por adelantado.

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 los sectores de listas hagan una copia es una característica de las listas. Si está recortando objetos avanzados como un Pandas DataFrame, puede devolver una vista en el original y no una copia.


184
2017-07-12 13:19



Y un par de cosas que no eran inmediatamente 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 quisieras, por alguna razón, cada segundo elemento en la secuencia invertida:

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

124
2018-02-03 23:15



Encontré esta gran mesa 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)

84
2017-09-06 06:50



En Python 2.7

Cortar 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 dice [a: b: c], usted está diciendo que dependiendo del signo de c (hacia adelante o hacia atrás), comience en a y termine en b (excluyendo el elemento en el bth índice). 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 de arriba mientras recorre usando las reglas para a, b, c de arriba, obtendrá una lista con los elementos (tocados durante el recorrido) o obtendrá una lista vacía.

Una última cosa: si a y b 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]

84
2017-10-22 05:33



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 ciclo for ...

(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 funciona para mí de todos modos ...


47
2018-02-19 20:52