2010-10-29 6 views
5

Esta es una pregunta simple sobre cómo maneja Python los datos y las variables. He hecho muchos experimentos y han descubierto la mayoría de Python, excepto que esta me mantiene tropezar:Asignación de un valor a un elemento de un sector en Python

[editar: Me separé y se han 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 explicarme qué está pasando aquí?

Hasta ahora, las respuestas parecen afirmar que a[0:1] devuelve una nueva lista que contiene una referencia al primer elemento de a. Pero no veo cómo eso explica el Ejemplo 1.

+1

Interesante pregunta. Veamos lo que dice Alex. :) –

Respuesta

7

a [0: 1] devuelve una nueva matriz que contiene una referencia a la matriz [1], por lo que termina modificando la matriz interna a través de una llamada de referencia .

La razón por la que el primer caso no modifica la matriz [1] es que está asignando a la matriz externa copiada un nuevo valor de matriz interna.

Línea inferior - a [0: 1] devuelve una copia de los datos, pero los datos internos no se copian.

+0

@ dln385: la última frase de esta publicación responde a su pregunta. Los datos internos (en su caso, el número) no se copian, por lo que modificarlo cambia el original. –

+0

La última oración explica por qué 'a [0: 1] [0] [0] = 5' funciona. Pero 'a [0: 1] = [[5]]' no está modificando los datos internos, por lo que _no debería funcionar. Creo que debe manejarse como un caso especial, como [pyfunc] (http://stackoverflow.com/questions/4055515/assigning-a-value-to-an-element-of-a-slice-in-python/ 4055556 # 4055556) parece implicar. – dln385

+4

Sí, cuando la declaración de asignación (es decir, la señal "=") viene después de una referencia de división es una cosa completamente diferente que cuando se realizan otras operaciones. Para ser claros: '>>> a [0: 1] = [[5]]' es equivalente a 'a .__ setitem __ (slice (0,1, None), [5])' --- while 'a [ 0: 1] [0] = 5' es lo mismo que: 'a .__ getitem __ (slice (0,1, None)) .__setitem __ (0, 5)' – jsbueno

3

Según mi entender, el corte de segmentos devuelve un nuevo objeto. Ese es su valor de retorno es una nueva lista.

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 

algunos más jugadas 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 también se conoce comúnmente como asignación de división El comportamiento de las listas integradas es atómico (eliminar + insertar) sucede de una vez. Según entiendo, esto no está permitido para la secuencia personalizada.

+0

Esto es correcto. Rebanar devuelve un nuevo objeto. Lo mismo es cierto para asignar claves de diccionario a una nueva variable (por ejemplo, 'foo = mydict [bar]'). Los cambios en el sector asignado no modifican la referencia original porque son una copia. – jathanism

1

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] utilizado como una expresión =>a.__getitem__(i)

Aquí a, b y i son expresiones, y i puede contener slice objects creado usando la sintaxis de taquigrafía de dos puntos. 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 

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 de eso, que luego se modifica utilizando __setitem__.

Su segundo ejemplo por el contrario se convierte en

a.__getitem__(slice(0,1)).__setitem__(0, 5) 

Así __setitem__ se está llamando en la copia en rodajas, dejando intacta la lista original.

Cuestiones relacionadas