43 votes

Le découpage de chaînes de caractères effectue-t-il une copie en mémoire ?

Je me demande si :

a = "abcdef"
b = "def"
if a[3:] == b:
    print("something")

réalise en fait une copie de la partie "def" de a quelque part dans la mémoire, ou si la vérification des lettres est effectuée sur place ?

Note : Je parle d'une chaîne de caractères, pas d'une liste (pour laquelle je connais la réponse).

40voto

wim Points 35274

Le découpage de chaînes de caractères fait une copie en CPython.

En regardant dans la source, cette opération est gérée dans unicodeobject.c:unicode_subscript . Il existe évidemment un cas spécial de réutilisation de la mémoire lorsque le pas est 1, le début est 0, et que le contenu entier de la chaîne est découpé en tranches - ceci entre dans la rubrique unicode_result_unchanged et il n'y aura pas de copie. Cependant, le cas général appelle PyUnicode_Substring où toutes les routes mènent à un memcpy .

Pour vérifier empiriquement ces affirmations, vous pouvez utiliser un outil de profilage de la mémoire stdlib tracemalloc :

# s.py
import tracemalloc

tracemalloc.start()
before = tracemalloc.take_snapshot()
a = "." * 7 * 1024**2  # 7 MB of .....   # line 6, first alloc
b = a[1:]                                # line 7, second alloc
after = tracemalloc.take_snapshot()

for stat in after.compare_to(before, 'lineno')[:2]:
    print(stat)

Vous devriez voir les deux premières statistiques s'afficher comme suit :

/tmp/s.py:6: size=7168 KiB (+7168 KiB), count=1 (+1), average=7168 KiB
/tmp/s.py:7: size=7168 KiB (+7168 KiB), count=1 (+1), average=7168 KiB

Ce résultat montre deux des allocations de 7 méga, preuve évidente de la copie de mémoire, et les numéros de ligne exacts de ces allocations seront indiqués.

Essayez de changer la tranche de b = a[1:] en b = a[0:] pour voir que le cas spécial de la chaîne entière est effectif : il ne devrait y avoir qu'une seule grande allocation maintenant, et sys.getrefcount(a) augmentera d'une unité.

En théorie, puisque les chaînes de caractères sont immuables, une implémentation pourrait réutiliser la mémoire pour les tranches de sous-chaînes. Cela compliquerait probablement tout processus de collecte des déchets basé sur le comptage des références, et ce n'est donc peut-être pas une idée utile en pratique. Considérez le cas où une petite tranche d'une chaîne beaucoup plus grande a été prise - à moins que vous n'implémentiez une sorte de comptage de sous-références sur la tranche, la mémoire de la chaîne beaucoup plus grande ne pourrait pas être libérée avant la fin de la durée de vie de la sous-chaîne.

Pour les utilisateurs qui ont spécifiquement besoin d'un type standard qui peut être découpé en tranches sans copier les données sous-jacentes, il y a memoryview . Ver Quel est l'intérêt de memoryview en Python ? pour plus d'informations à ce sujet.

2voto

lorenzozane Points 1206

Sujet de discussion possible (n'hésitez pas à modifier l'ajout d'informations).

J'ai juste écrit ce test pour vérifier empiriquement ce que pourrait être la réponse à la question (cela ne peut et ne veut pas être une réponse certaine).

import sys

a = "abcdefg"

print("a id:", id(a))
print("a[2:] id:", id(a[2:]))
print("a[2:] is a:", a[2:] is a)

print("Empty string memory size:", sys.getsizeof(""))
print("a memory size:", sys.getsizeof(a))
print("a[2:] memory size:", sys.getsizeof(a[2:]))

Sortie :

a id: 139796109961712
a[2:] id: 139796109962160
a[2:] is a: False
Empty string memory size: 49
a memory size: 56
a[2:] memory size: 54

Comme on peut le voir ici :

  • la taille d'un objet chaîne vide est de 49 octets
  • un seul caractère occupe 1 octet (codage latin-1)
  • a y a[2:] les identifiants sont différents
  • la mémoire occupée de chaque a y a[2:] est compatible avec la mémoire occupée par une chaîne de caractères avec ce nombre de caractères assignés

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X