La asignación de un valor a un elemento de una rebanada en Python
-
27-09-2019 - |
Pregunta
Esta es una simple cuestión de cómo Python maneja los datos y variables. He hecho muchos experimentos y han descubierto la mayoría de Python, excepto que esta me mantiene tropezar:
[Editar: I separó y reorganizado los ejemplos para mayor claridad]
Ejemplo 1:
>>> a = [[1], 2]
>>> a[0:1]
[[1]]
>>> a[0:1] = [[5]]
>>> a
[[5], 2] # The assignment worked.
Ejemplo 2:
>>> a = [[1], 2]
>>> a[0:1][0]
[1]
>>> a[0:1][0] = [5]
>>> a
[[1], 2] # No change?
Ejemplo 3:
>>> a = [[1], 2]
>>> a[0:1][0][0]
1
>>> a[0:1][0][0] = 5
>>> a
[[5], 2] # Why now?
Puede alguien me explique lo que está pasando aquí?
Hasta ahora, las respuestas parecen afirmar que a[0:1]
devuelve una lista nueva que contiene una referencia al primer elemento de a
. Pero no veo cómo eso explica el ejemplo 1.
Solución
a [0: 1]. Devuelve una nueva matriz que contiene una referencia a la matriz de [1], por lo que se termina la modificación de la matriz interna a través de una llamada de referencia
La razón el primer caso no modifica la [1] serie es que se está asignando la matriz externa copiado un nuevo valor de la matriz interior.
Fondo línea - a. [0: 1] devuelve una copia de los datos, pero los datos interna no se copia
Otros consejos
Mi entendimiento es cortar devuelve un nuevo objeto. Es decir que del valor de retorno es una lista nueva.
Por lo tanto no se puede utilizar un operador de asignación a los cambios de los valores de la lista original
>>> a = [[1], 2, 3]
>>> k = a[0:2]
>>> id(a)
4299352904
>>> id(k)
4299353552
>>>
>>> id(a)
4299352904
>>> id(a[0:2])
4299352832
algunas jugadas más a lo largo de las líneas
>>> k = 5
>>>
>>> id(k)
4298182344
>>> a[0] = [1,2]
>>> a
[[1, 2], 2, 3]
>>> id(a)
4299352904
>>>
[Editar: en la segunda parte de la pregunta]
>>> a[0:1] = [[5]]
La siguiente notación se denomina también comúnmente como asignación rebanada El comportamiento de las listas incorporadas es atómica (delete + insert) ocurre de una sola vez. Mi opinión es que esto no está permitido para la secuencia personalizada.
Hay tres operaciones distintas con índices, todas son traducidas a llamadas de método:
-
a[i] = b
=>a.__setitem__(i, b)
-
del a[i]
=>a.__delitem__(i)
-
a[i]
utiliza como una expresión =>a.__getitem__(i)
Aquí a
, b
y i
son expresiones y i
pueden contener objetos de división creado utilizando la sintaxis abreviada de colon. Por ejemplo:.
>>> class C(object):
... def __setitem__(self, *a):
... print a
...
>>> C()[1] = 0
(1, 0)
>>> C()['foo'] = 0
('foo', 0)
>>> C()['foo':'bar'] = 0
(slice('foo', 'bar', None), 0)
>>> C()['foo':'bar',5] = 0
((slice('foo', 'bar', None), 5), 0)
Entonces, ¿qué está sucediendo en su tercer ejemplo es la siguiente:
a[0:1][0][0] = 5
se convierte
a.__getitem__(slice(0,1)).__getitem__(0).__setitem__(0, 5)
La primera __getitem__
devuelve una copia de parte de la lista, pero la segunda __getitem__
devuelve la lista real dentro que, que se modifica a continuación, utilizando __setitem__
.
Su segundo ejemplo por el contrario se convierte
a.__getitem__(slice(0,1)).__setitem__(0, 5)
Así __setitem__
está siendo llamado en la copia en rodajas, dejando intacta la lista original.