Cortar una lista en Python sin generar una copia

Tengo el siguiente problema.

Dada una lista de enteros L , necesito generar todas las sublistas L[k:] for k in [0, len(L) - 1] , sin generar copias .

¿Cómo logro esto en Python? ¿Con un objeto buffer de alguna manera?

La respuesta corta

El corte de listas no genera copias de los objetos en la lista; sólo copia las referencias a ellos. Esa es la respuesta a la pregunta formulada.

La respuesta larga

Pruebas en valores mutables e inmutables.

En primer lugar, vamos a probar la afirmación básica. Podemos mostrar que incluso en el caso de objetos inmutables como los enteros, solo se copia la referencia. Aquí hay tres objetos enteros diferentes, cada uno con el mismo valor:

 >>> a = [1000 + 1, 1000 + 1, 1000 + 1] 

Tienen el mismo valor, pero puedes ver que son tres objetos distintos porque tienen diferentes id s:

 >>> map(id, a) [140502922988976, 140502922988952, 140502922988928] 

Al cortarlos, las referencias siguen siendo las mismas. No se han creado nuevos objetos:

 >>> b = a[1:3] >>> map(id, b) [140502922988952, 140502922988928] 

El uso de diferentes objetos con el mismo valor muestra que el proceso de copia no se molesta con la internación , simplemente copia directamente las referencias.

Las pruebas con valores mutables dan el mismo resultado:

 >>> a = [{0: 'zero', 1: 'one'}, ['foo', 'bar']] >>> map(id, a) [4380777000, 4380712040] >>> map(id, a[1:] ... ) [4380712040] 

Examinar la sobrecarga de memoria restante

Por supuesto las propias referencias son copiadas. Cada uno cuesta 8 bytes en una máquina de 64 bits. Y cada lista tiene su propia sobrecarga de memoria de 72 bytes:

 >>> for i in range(len(a)): ... x = a[:i] ... print('len: {}'.format(len(x))) ... print('size: {}'.format(sys.getsizeof(x))) ... len: 0 size: 72 len: 1 size: 80 len: 2 size: 88 

Como Joe Pinsonault nos recuerda , esa sobrecarga se acumula. Y los objetos enteros en sí mismos no son muy grandes, son tres veces más grandes que las referencias. Entonces, esto le ahorra algo de memoria en un sentido absoluto, pero asintóticamente, podría ser bueno tener varias listas que sean “vistas” en la misma memoria.

Guardar memoria utilizando vistas

Desafortunadamente, Python no proporciona una manera fácil de producir objetos que son “vistas” en listas. O tal vez debería decir “afortunadamente”! Esto significa que no tiene que preocuparse por el origen de una porción; los cambios en el original no afectarán la rebanada. En general, eso hace que el razonamiento sobre el comportamiento de un progtwig sea mucho más fácil.

Si realmente desea ahorrar memoria trabajando con vistas, considere utilizar matrices numpy . Cuando corta una matriz numpy , la memoria se comparte entre la división y el original:

 >>> a = numpy.arange(3) >>> a array([0, 1, 2]) >>> b = a[1:3] >>> b array([1, 2]) 

¿Qué sucede cuando modificamos a y miramos de nuevo a b ?

 >>> a[2] = 1001 >>> b array([ 1, 1001]) 

Pero esto significa que tiene que estar seguro de que cuando modifica un objeto, no está modificando involuntariamente otro. Esa es la compensación cuando se usa numpy : menos trabajo para la computadora y más trabajo para el progtwigdor.

Dependiendo de lo que estés haciendo, podrías usar islice .

Dado que funciona mediante iteración, no creará nuevas listas, sino que simplemente creará iteradores que yield elementos de la lista original según lo solicitado para sus rangos.