Couper une liste en Python sans générer de copie

j'ai le problème suivant.

avec une liste d'entiers L , je dois générer toutes les sous-listes L[k:] for k in [0, len(L) - 1] , sans générer de copies .

Comment puis-je accomplir ceci en Python? Avec un tampon d'objet en quelque sorte?

43
demandé sur nbro 2011-02-27 07:54:23

2 réponses

La réponse courte

Slicking lists ne génère pas de copies des objets de la liste; il ne fait que Copier les références à ceux-ci. C'est la réponse à la question posée.

la longue réponse

test sur valeurs mutables et immuables

tout d'abord, nous allons tester la revendication de base. Nous pouvons montrer que même dans le cas d'objets immuables comme des entiers, seule la référence est copiée. Voici trois objets entiers différents, chacun avec la même valeur:

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

ils ont la même valeur, mais vous pouvez voir qu'ils sont trois objets distincts parce qu'ils ont différent id s:

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

Lorsque vous tranchez, les références restent les mêmes. Aucun nouvel objet n'a été créé:

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

L'utilisation d'objets différents avec la même valeur montre que le processus de copie ne prend pas la peine de interning -- il copie directement les références.

L'essai avec des valeurs mutables donne le même résultat:

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

Examining remaining remember overhead

bien sûr les références elles-mêmes sont copiées. Chacune coûte 8 octets sur une machine 64 bits. Et chaque liste a sa propre mémoire au-dessus de 72 octets:

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

Comme Joe Pinsonault nous rappelle , que les frais généraux s'additionnent. Et les objets entiers eux-mêmes ne sont pas très grands -- ils sont trois fois plus grands que les références. Donc cela vous sauve un peu de mémoire dans un sens absolu, mais asymptotiquement, il pourrait être agréable d'être en mesure d'avoir plusieurs listes qui sont "vues" dans la même mémoire.

sauvegarder la mémoire en utilisant des vues

malheureusement, Python ne fournit aucun moyen facile de produire des objets qui sont "points de vue" dans des listes. Ou peut-être devrais-je dire "heureusement"! Cela signifie que vous n'avez pas à vous soucier d'où une tranche vient; des modifications à l'original n'affectera pas la tranche. Dans l'ensemble, cela rend le raisonnement sur le comportement d'un programme beaucoup plus facile.

si vous voulez vraiment sauver de la mémoire en travaillant avec des vues, envisagez d'utiliser des tableaux numpy . Lorsque vous découpez un tableau numpy , la mémoire est partagée entre la tranche et l'original:

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

que se passe-t-il quand on modifie a et qu'on regarde à nouveau b ?

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

mais cela signifie que vous devez être sûr que lorsque vous modifiez un objet, vous ne modifiez pas par inadvertance un autre. C'est le compromis lorsque vous utilisez numpy : moins de travail pour l'ordinateur, et plus de travail pour le programmeur!

69
répondu senderle 2017-05-23 12:18:36

selon ce que vous faites, vous pourriez être en mesure d'utiliser islice .

puisqu'il fonctionne par itération, il ne fera pas de nouvelles listes, mais créera simplement des itérateurs que yield éléments de la liste originale comme demandé pour leurs gammes.

15
répondu Amber 2016-11-13 12:06:39