¿Cómo funciona la asignación con el segmento de lista de Python?

100

El documento de Python dice que cortar una lista devuelve una nueva lista.
Ahora, si se devuelve una lista "nueva", tengo las siguientes preguntas relacionadas con "Asignación a sectores".

a = [1, 2, 3]
a[0:2] = [4, 5]
print a

Ahora la salida sería:

[4, 5, 3] 
  1. ¿Cómo puede algo que está devolviendo algo aparecer en el lado izquierdo de la expresión?
  2. Sí, leí los documentos y dice que es posible, ahora que al cortar una lista se devuelve una lista "nueva", ¿por qué se modifica la lista original? No puedo entender la mecánica detrás de esto.
Kartik Anand
fuente
@Mark Longair lo siento, pensé que solo se supone que el código debe formatearse, no la salida
Kartik Anand
7
¿Entiendes lo a[0] = 4que haría?
Josh Lee
1
La asignación de @KartikAnand Slice es un escenario especial en el que no se crea una nueva lista. No tiene sentido crear un objeto sin un enlace de nombre en el lado izquierdo de un =, por lo que en lugar de descartarlo como sintaxis no válida, Python lo convierte en algo más parecido a lo que podría esperar. Dado que Python no tiene referencias, no funcionaría que el resultado de un segmento cambie la lista original. Obtienes una copia. Si proporcionó más información sobre su aplicación, es posible que podamos ayudarlo mejor a hacer las cosas de la manera 'pitónica'. :)
Casey Kuball
1
@Darthfett No estoy trabajando en ninguna aplicación en este momento, sino que estoy aprendiendo Python antes de comenzar a ensuciarme las manos :)
Kartik Anand

Respuestas:

114

Está confundiendo dos operaciones distintas que utilizan una sintaxis muy similar:

1) rebanado:

b = a[0:2]

Esto hace una copia del segmento de ay lo asigna a b.

2) asignación de cortes:

a[0:2] = b

Esto reemplaza la porción de acon el contenido de b.

Aunque la sintaxis es similar (¡me imagino por diseño!), Estas son dos operaciones diferentes.

NPE
fuente
4
Esa es mi duda, en el segundo caso, ¿por qué no es la porción de una nueva lista?
Kartik Anand
11
@KartikAnand Porque no lo es. Eso no es lo que especifica el idioma.
Marcin
Para ser claros, "toma una porción de" realmente significa "hacer una copia de una porción de", de donde proviene parte de la confusión.
Mark Ransom
2
@KartikAnand: Básicamente, sí. El intérprete sabe cuál es cuál y los maneja adecuadamente.
NPE
1
@Dubslow: puede hacerlo utilizando el módulo itertools . Para su caso, utilice la función iSlice , con start=1, stop=None. Esto evitará copias y utilizará la evaluación diferida (en su caso, el acceso diferido a la lista original).
Spiros
66

Cuando especifica aen el lado izquierdo del =operador, está usando la asignación normal de Python , que cambia el nombre aen el contexto actual para apuntar al nuevo valor. Esto no cambia el valor anterior al que aapuntaba.

Al especificar a[0:2]en el lado izquierdo del =operador, le está diciendo a Python que desea usar la asignación de sectores . Slice Assignment es una sintaxis especial para listas, donde puede insertar, eliminar o reemplazar contenido de una lista:

Inserción :

>>> a = [1, 2, 3]
>>> a[0:0] = [-3, -2, -1, 0]
>>> a
[-3, -2, -1, 0, 1, 2, 3]

Supresión :

>>> a
[-3, -2, -1, 0, 1, 2, 3]
>>> a[2:4] = []
>>> a
[-3, -2, 1, 2, 3]

Reemplazo :

>>> a
[-3, -2, 1, 2, 3]
>>> a[:] = [1, 2, 3]
>>> a
[1, 2, 3]

Nota:

La longitud del corte puede ser diferente de la longitud de la secuencia asignada, cambiando así la longitud de la secuencia objetivo, si la secuencia objetivo lo permite. - fuente

Slice Assignment proporciona una función similar a Tuple Unpacking . Por ejemplo, a[0:1] = [4, 5]es equivalente a:

# Tuple Unpacking
a[0], a[1] = [4, 5]

Con Tuple Unpacking, puede modificar listas no secuenciales:

>>> a
[4, 5, 3]
>>> a[-1], a[0] = [7, 3]
>>> a
[3, 5, 7]

Sin embargo, el desembalaje de tuplas se limita al reemplazo, ya que no puede insertar ni quitar elementos.

Antes y después de todas estas operaciones, aes la misma lista exacta. Python simplemente proporciona un agradable azúcar sintáctico para modificar una lista en el lugar.

Casey Kuball
fuente
6
Similar pero no idéntico, ya que puede tener un número desigual de elementos a la izquierda y a la derecha.
Mark Ransom
@MarkRansom Ese es un punto excelente, he agregado más información para que esto sea obvio.
Casey Kuball
2
¿Es a[:] = some_listequivalente a a = some_list[:]o a = some_list?
jadkik94
2
@ jadkik94 Ninguno. a[:] = some_listestablece todos los elementos de acomo los de some_list. Hacer cualquiera de los que mencionas cambiaría lo que aes. Por ejemplo: a = [1, 2, 3] b = a a[:] = [4, 5, 6] a is b. La última línea sería False si cambiara ael valor, en lugar de mutarlo.
Casey Kuball
@Darthfett Interesante, había descubierto lo contrario :) Gracias.
jadkik94
25

Me encontré con la misma pregunta antes y está relacionada con la especificación del idioma. Según declaraciones de asignación ,

  1. Si el lado izquierdo de la asignación es una suscripción, Python llamará __setitem__a ese objeto. a[i] = xes equivalente a a.__setitem__(i, x).

  2. Si el lado izquierdo de la asignación es slice, Python también llamará __setitem__, pero con diferentes argumentos: a[1:4]=[1,2,3]es equivalente a a.__setitem__(slice(1,4,None), [1,2,3])

Es por eso que el segmento de lista en el lado izquierdo de '=' se comporta de manera diferente.

Stan
fuente
4

Al cortar en el lado izquierdo de una operación de asignación, está especificando a qué elementos asignar.

fraxel
fuente