2010-07-02 3 views
5

Comme tout programmeur Python le sait, vous devez utiliser == au lieu de is pour comparer deux chaînes pour l'égalité. Cependant, y a-t-il réellement des cas où (s is "") et (s == "") donneront des résultats différents dans Python 2.6.2?Can (s est "") et (s == "") donnent jamais des résultats différents dans Python 2.6.2?

Je suis récemment tombé sur du code qui utilisait (s is "") dans la revue de code, et tout en soulignant que c'était incorrect, je voulais donner un exemple de comment cela pourrait échouer. Mais essaye comme je pourrais, je ne peux pas construire deux chaînes vides avec des identités différentes. Il semble que l'implémentation de Python doit faire un cas particulier de la chaîne vide dans beaucoup d'opérations communes. Par exemple:

>>> a = "" 
>>> b = "abc"[ 2:2 ] 
>>> c = ''.join([]) 
>>> d = re.match('()', 'abc').group(1) 
>>> e = a + b + c + d 
>>> a is b is c is d is e 
True 

Cependant, this question suggère qu'il y sont cas où (s is "") et (s == "") peuvent être différents. Quelqu'un peut-il me donner un exemple?

Répondre

11

Comme tout le monde l'a dit, ne comptez pas sur un comportement indéfini. Toutefois, étant donné que vous avez demandé un contre spécifique pour Python 2.6, la voici:

>>> s = u"\xff".encode('ascii', 'ignore') 
>>> s 
'' 
>>> id(s) 
10667744 
>>> id("") 
10666064 
>>> s == "" 
True 
>>> s is "" 
False 
>>> type(s) is type("") 
True 

La seule fois que Python 2.6 peut se retrouver avec une chaîne vide qui n'est pas la chaîne vide normale est quand il fait une chaîne opération et il n'est pas sûr à l'avance combien de temps la chaîne sera. Ainsi, lorsque vous codez une chaîne, le gestionnaire d'erreurs peut finir par effacer les caractères et corriger la taille de la mémoire tampon une fois la chaîne terminée. Bien sûr, c'est un oubli et pourrait facilement changer dans Python 2.7.

12

Python is teste l'identité des objets et non l'égalité. Voici un exemple où l'utilisation is et == donne un résultat différent:

>>> s=u"" 
>>> print s is "" 
False 
>>> print s=="" 
True 
+1

Cela permet cependant de tester deux types d'objets chaîne différents, 'str' et' unicode'. La question subsiste quand s est du même type, ou si vous utilisez Python 3 dans lequel il n'y a plus d'objet explicite 'unicode'. – poke

+1

@poke, La question est explicitement centrée sur Python 2.6.2 et non sur Python 3. – zoli2k

+0

C'est vrai, mais c'est une distinction importante à faire et ce n'est probablement pas la façon la plus amicale de le faire. Quel test était réellement signifié par ce code? Que «la chose passée était équivalente à une chaîne vide» ou que «la chose passée était de type str et vide»? Imo si c'était le dernier alors un test de type explicite serait plus convivial pour les futurs responsables et utilisateurs. – pycruft

3

Il semble fonctionner pour tout ce qui est en fait une chaîne, mais quelque chose qui vient ressemble une chaîne (par exemple un unicode ou sous-classe de str ou quelque chose de similaire) échouera.

>>> class mysub(str): 
    def __init__(self, *args, **kwargs): 
     super(mysub, self).__init__(*args, **kwargs) 

>>> 
>>> q = mysub("") 
>>> q is "" 
False 
>>> q == "" 
True 

modifier:

Aux fins de l'examen du code & commentaires Je dirais que ce fut une mauvaise pratique, car il met en oeuvre un test inattendu (même si on fait abstraction de l'incertitude de savoir si elle se comportera toujours la même chose lorsque les types correspondent).

if x is "" 

Implique que x est la valeur correcte et le type, mais sans un test explicite de type qui avertirait future mainteneurs ou les utilisateurs de api, etc.

if x == "" 

Implique que x est juste de la valeur correcte

+0

Et bien sûr ils seront différents pour un objet d'une classe non liée à str mais qui implémente \ _ \ _ eq \ _ \ _ de façon à comparer égale à une chaîne. – jchl

7

Cela ne devrait pas vous intéresser. Contrairement à None qui est défini comme un singleton, aucune règle ne dit qu'il n'y a qu'un seul objet chaîne vide. Donc, le résultat de s is "" dépend de l'implémentation et l'utilisation de is est NON-NO si vous pouvez trouver un exemple ou non.

+0

Je comprends cela. Mais dans le but de donner une rétroaction de révision de code à un programmeur junior, il vaut mieux dire "ne faites pas ça parce que ça ne marche pas dans cette situation" que de dire "ne fais pas ça parce que c'est faux". Bien sûr, si "parce que c'est faux" est la seule raison, alors c'est la raison pour laquelle je devrais donner ... – jchl

+1

Eh bien, vous devriez donner la grande image à votre programmeur junior. Il est préférable de savoir que 'is' teste quelque chose d'autre que' == 'et devrait donc être utilisé pour son bon usage - le fait fortuit que vous pouvez vous en sortir dans quelques circonstances est plutôt hors de propos - essayer d'apprendre le Les circonstances dans lesquelles un antipattern * does * work est un effort gaspillé et une recette infaillible pour de futurs bugs. –

+0

Vous devriez dire: "Ne faites pas ça parce que c'est faux, c'est faux car' is' ne devrait pas être utilisé pour vérifier si deux valeurs sont égales, seulement pour vérifier si elles sont exactement la même instance. " – Blixt

0

Probablement non, CPython semble optimiser les instances parasites de "" dans tous les cas. Mais comme disent les autres, ne comptez pas là-dessus.

1

Vous ne pouvez pas trouver d'exemple parce que certaines choses sont uniques et non mutables - donc Python les conserve exactement une fois et donc is fonctionne. Ceux-ci incluent (), '',u'', True, False, None CPython garde même quelques nombres fréquemment utilisés, ie 0, 0.0, 1, 1.0,

+2

Expérimentalement, il semble que CPython (version 2.6.2) intègre tous les entiers de -5 à 256. – jchl

+0

'0.0' et' 1.0' ne devraient pas être dans la liste ci-dessus: les floats ne sont pas internés par CPython actuel. –

0

Le comportement non défini est un problème obscur. Il y a des choses que la spécification Python définit et auxquelles les implémentations adhérentes doivent se conformer, et il reste des choses à choisir.Vous pouvez être convaincu, en regardant dans le code source de Python, que ce comportement ne peut jamais arriver pour les objets string réels (contrairement à unicode vs. non-unicode et d'autres exemples proches mais non-pertinents). Heureux, vous laisserez un tel test dans un code.

Mais l'implémentation Python ne garantit pas qu'elle fonctionnera toujours. Une implémentation future peut le faire changer et vous aurez une incompatibilité douloureuse.

Donc, la règle de base avec ceci est simple: ne le faites pas. Utilisez les opérateurs uniquement pour leur utilisation prévue et bien documentée. Ne comptez pas sur les artefacts de mise en œuvre qui pourraient très bien changer à l'avenir.

Questions connexes