117 votes

Stage sur les chaînes de caractères Python

Bien que cette question n'ait pas d'utilité réelle dans la pratique, je suis curieux de savoir comment Python s'y prend pour faire un stage en chaîne. J'ai remarqué ce qui suit.

>>> "string" is "string"
True

C'est ce que j'attendais.

Vous pouvez également faire ceci.

>>> "strin"+"g" is "string"
True

Et c'est très astucieux !

Mais vous ne pouvez pas faire cela.

>>> s1 = "strin"
>>> s2 = "string"
>>> s1+"g" is s2
False

Pourquoi Python n'évaluerait-il pas s1+"g" et se rendre compte qu'il s'agit de la même chose que s2 et le diriger vers la même adresse ? Que se passe-t-il réellement dans ce dernier bloc pour qu'il renvoie False ?

118voto

NPE Points 169956

Cela dépend de l'implémentation, mais votre interpréteur interagit probablement avec les constantes de compilation, mais pas avec les résultats des expressions d'exécution.

Dans ce qui suit, CPython 3.9.0+ est utilisé.

Dans le deuxième exemple, l'expression "strin"+"g" est évaluée à la compilation et est remplacée par "string" . Les deux premiers exemples se comportent donc de la même manière.

Si nous examinons les bytecodes, nous verrons qu'ils sont exactement les mêmes :

  # s1 = "string"
  1           0 LOAD_CONST               0 ('string')
              2 STORE_NAME               0 (s1)

  # s2 = "strin" + "g"
  2           4 LOAD_CONST               0 ('string')
              6 STORE_NAME               1 (s2)

Ce bytecode a été obtenu avec (qui imprime quelques lignes supplémentaires après ce qui précède) :

import dis

source = 's1 = "string"\ns2 = "strin" + "g"'
code = compile(source, '', 'exec')
print(dis.dis(code))

Le troisième exemple concerne une concaténation au moment de l'exécution, dont le résultat n'est pas automatiquement interné :

  # s3a = "strin"
  3           8 LOAD_CONST               1 ('strin')
             10 STORE_NAME               2 (s3a)

  # s3 = s3a + "g"
  4          12 LOAD_NAME                2 (s3a)
             14 LOAD_CONST               2 ('g')
             16 BINARY_ADD
             18 STORE_NAME               3 (s3)
             20 LOAD_CONST               3 (None)
             22 RETURN_VALUE

Ce bytecode a été obtenu avec (qui imprime quelques lignes supplémentaires avant ce qui précède, et ces lignes sont exactement les mêmes que dans le premier bloc de bytecodes donné ci-dessus) :

import dis

source = (
    's1 = "string"\n'
    's2 = "strin" + "g"\n'
    's3a = "strin"\n'
    's3 = s3a + "g"')
code = compile(source, '', 'exec')
print(dis.dis(code))

Si vous deviez manuellement sys.intern() le résultat de la troisième expression, vous obtiendrez le même objet que précédemment :

>>> import sys
>>> s3a = "strin"
>>> s3 = s3a + "g"
>>> s3 is "string"
False
>>> sys.intern(s3) is "string"
True

De plus, Python 3.9 affiche un avertissement pour les deux dernières instructions ci-dessus :

Avertissement syntaxique : "est" avec un littéral. Vouliez-vous dire "==" ?

31 votes

Et pour mémoire : L'optimisation du trou de souris de Python précalculera les opérations arithmétiques sur les constantes ( "string1" + "s2" , 10 + 3*20 etc.) au moment de la compilation, mais limite les résultats de l'utilisation des séquences à seulement 20 éléments (pour éviter que les [None] * 10**1000 de ne pas trop développer votre bytecode). C'est cette optimisation qui a effondré "strin" + "g" en "string" ; le résultat est inférieur à 20 caractères.

17 votes

Et pour être tout à fait clair : il n'y a pas du tout de stage ici. Les littéraux immuables sont stockés en tant que constantes dans le bytecode. Interne fait s'applique aux noms utilisés dans le code, mais pas aux chaînes de caractères créées par le programme, à moins qu'elles ne soient spécifiquement internalisées par l'élément intern() fonction.

10 votes

Pour ceux qui essaient de trouver intern dans Python 3 - elle est déplacée vers la fonction sys.intern

5voto

cppcoder Points 2941

Cas 1

>>> x = "123"  
>>> y = "123"  
>>> x == y  
True  
>>> x is y  
True  
>>> id(x)  
50986112  
>>> id(y)  
50986112  

Cas 2

>>> x = "12"
>>> y = "123"
>>> x = x + "3"
>>> x is y
False
>>> x == y
True

Maintenant, votre question est de savoir pourquoi l'identifiant est le même dans le cas 1 et pas dans le cas 2.
Dans le cas 1, vous avez attribué un littéral de chaîne de caractères "123" a x y y .

Les chaînes de caractères étant immuables, il est logique que l'interprète ne stocke le littéral de la chaîne qu'une seule fois et que toutes les variables pointent vers le même objet.
C'est pourquoi vous considérez les id comme identiques.

Dans le cas 2, vous modifiez x en utilisant la concaténation. Les deux x y y a les mêmes valeurs, mais pas la même identité.
Tous deux renvoient à des objets différents dans la mémoire. Ils ont donc des id y is opérateur retourné False

0 votes

Puisque les chaînes de caractères sont immuables, comment se fait-il que l'assignation de x+"3" (et la recherche d'un nouvel emplacement pour stocker la chaîne) n'affecte pas la même référence que y ?

0 votes

En effet, il doit alors comparer la nouvelle chaîne avec toutes les chaînes existantes, ce qui peut s'avérer une opération très coûteuse. Il pourrait le faire en arrière-plan après l'affectation, je suppose, pour réduire la mémoire, mais on obtiendrait alors un comportement encore plus étrange : id(x) != id(x) par exemple, parce que la chaîne a été déplacée au cours du processus d'évaluation.

1 votes

@AndreaConte parce que la concaténation des chaînes ne fait pas le travail supplémentaire de chercher dans le pool de toutes les chaînes utilisées à chaque fois qu'elle en génère une nouvelle. D'autre part, l'interpréteur "optimise" l'expression x = "12" + "3" en x = "123" (concaténation de deux chaînes de caractères littérales dans une seule expression), de sorte que l'affectation effectue la recherche et trouve la même chaîne "interne" que dans le cas de y = "123" .

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