41
votes

Le découpage de la chaîne effectue-t-il une copie en mémoire?

Je me demande si:

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

effectue réellement une copie de la partie "def" de a quelque part en mémoire, ou si la vérification des lettres est fait en place?

Remarque: Je parle d'une chaîne, pas d'une liste (pour laquelle je connais la réponse)


10 commentaires

a [3:] crée un nouvel objet utilisant un nouvel espace mémoire.


@Klausd. êtes-vous sûr de cela? J'ai toujours su que le tranchage a l'avantage de sauvegarder uniquement les références à l'élément d'origine et même si la sauvegarde des références est une action qui occupe également la mémoire, elle n'est pas comparable à parler d'une copie des éléments. Pouvez-vous expliquer un peu plus?


@Klausd. - C'est une question intéressante, cependant. C'est un nouvel objet, mais fait-il référence à la même mémoire physique que la première chaîne ou les données elle-même sont-elles copiées? Je suis sûr que Python copie les données même si l'on pense que les tranches ne sont que des vues dans la mémoire de chaîne immuable, mais je ne trouve pas de référence.


Il est facile de tester: faire une tranche, changer l'original, voir que la tranche n'a pas changé. (Ne fonctionne pas sur les piqûres car ils ne sont pas mutables.)


@Lorenzozane "découpage" est ce que le type le définit. Dans le cas des chaînes, il crée un nouvel objet de chaîne. Le temps d'exécution est libre d'optimiser les chaînes de diverses manières car elles sont immuables, mais le trancher en fait une copie.


@ Juanpa.arrivillaga l'a obtenu, merci!


Faire une référence à une sous-chaîne de la chaîne existante (sans copie) économiserait sur la mémoire dans certaines circonstances mais pas d'autres; Par exemple, si vous avez une énorme chaîne et que vous ne gardez qu'une petite sous-chaîne, alors l'énorme chaîne ne peut être collectée aux ordures que si la sous-chaîne est une copie.


@ kaya3 Oui, pour le cas d'utilisation spécialisé, les objets MemoryView existent.


@ user2357112 Je pense que cela devrait être dupé dans l'autre sens.


@wim je pense qu'il serait préférable de demander un mod à fusionner .


2 Réponses :


2
votes

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

Je viens d'écrire ce test pour vérifier empiriquement ce que pourrait être la réponse à la question (cela ne peut pas et ne veut pas et ne veut pas être une certaine réponse).

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

sortie:

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:]))

Comme nous pouvons le voir ici: p >

  • La taille d'un objet de chaîne vide est de 49 octets
  • Un seul caractère occupe 1 octet (codage latin-1)
  • a et a [2:] Les ID sont différents
  • La mémoire occupée de chaque a et a [2:] est cohérente avec la mémoire occupée par une chaîne avec ce nombre de char affecté


5 commentaires

Merci, j'ai essayé avec des chaînes plus longues pour assurer la cohérence. Vous semblez tout à fait juste!


Cela ne prouve pas que Python n'utilise pas la même mémoire pour la sous-chaîne. Les pyobjects sous-jacents pourraient, en théorie, réutiliser la mémoire sans contradiser tout ce que vous avez montré ici (notez que l'ID étant lié à l'adresse mémoire est un détail d'implémentation de CPython, et ils se réfèrent à l'adresse de l'en-tête PyObject de toute façon, pas nécessairement la mémoire Emplacements du stockage de données sous-jacent pour un objet Unicode)


En effet, je change le drapeau de réponse (mais merci quand même à Lorenzo Zane, mon esprit a changé avec le vôtre)


@Wim Comme je l'ai écrit clairement, ma réponse est censée être un signal pour la discussion. Pas quelque chose de certain, car je ne suis pas en mesure de donner une certaine réponse. J'aimerais que vous en discutiez pour une amélioration possible.


@Lorenzozane Oui c'est ce que je fais.



39
votes

STRING SINCING fait une copie dans cpython.

En regardant dans la source, cette opération est gérée dans UnicodeObject.c: Unicode_subscript . Il y a évidemment un cas spécial pour réutiliser la mémoire lorsque l'étape est 1, le démarrage est 0, et le contenu entier de la chaîne est tranché - cela va dans uncode_result_unchanged et il n'y aura pas de copie. Cependant, l'affaire générale appelle pyunde_substring> codefor / a> où toutes les routes mènent à un memcpy .

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

/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


2 commentaires

Ah oui, le cas spécial de s [:] est s


Fait intéressant, JDK avait l'habitude d'avoir des sous-chaînes non co-colorées, mais elle a été modifiée pour correspondre précisément au comportement de Cpython pour la raison que vous avez mentionné ("Considérez le cas où ...")