2010-11-04 2 views
6

Может ли кто-нибудь объяснить мне этот странный результат на python 2.6.6?Странный результат в python

>>> a = "xx" 
>>> b = "xx" 
>>> a.__hash__() == b.__hash__() 
True 
>>> a is b 
True # ok.. was just to be sure 

>>> a = "x" * 2 
>>> b = "x" * 2 
>>> a.__hash__() == b.__hash__() 
True 
>>> a is b 
True # yeah.. looks ok so far ! 

>>> n = 2 
>>> a = "x" * n 
>>> b = "x" * n 
>>> a.__hash__() == b.__hash__() 
True # still okay.. 
>>> a is b 
False # hey! What the F... ? 
+5

Где, черт возьми, люди узнают о 'is', но не о том, как он отличается от' == '? – delnan

+0

Возможный дубликат [Python '==' vs 'is' Сравнение строк, 'иногда не получается, почему?] (Http://stackoverflow.com/questions/1504717/python-vs-is-comparing-strings-is -fails-why-why) – SilentGhost

+0

@SilentGhost: Не совсем так, поскольку это касается темы, когда компиляторы могут неожиданно запускать строки. –

ответ

12

Чтобы понять это, вам нужно понять несколько разных вещей.

  • a is b возвращает истину, если a и b являются тот же объект, а не только, если они имеют одинаковое значение. Строки могут иметь одинаковое значение, но быть другим экземпляром этого значения.
  • Когда вы говорите a = "x", то, что вы на самом деле делаете, создает константу строки "x", а затем присваивает ей имя, a. Строки константы - это строки, которые буквально записаны в коде и не вычисляются программно. Строковые константы всегда интернированы, что означает, что они хранятся в таблице для повторного использования: если вы говорите a = "a"; b = "a", это на самом деле то же самое, что и a = "a"; b = a, так как они будут использовать ту же интернированную строку "a". Вот почему первый a is b имеет значение True.
  • Когда вы говорите a = "x" * 2, компилятор Python фактически оптимизирует это. Он вычисляет строку во время компиляции - генерирует код , как будто вы написали a = "xx". Таким образом, результирующая строка "xx' интернирована. Вот почему второй a is b прав.
  • Когда вы говорите a = "x" * n, компилятор Python не знает, что n во время компиляции. Поэтому он вынужден вывести строку "x", а затем выполнить умножение строки во время выполнения.Поскольку это выполняется во время выполнения, в то время как "x" интернирован, итоговая строка "xx" is не. В результате каждая из этих строк представляет собой разные экземпляры "xx", поэтому окончательный a is b - False.

Вы можете увидеть разницу сами:

def a1(): 
    a = "x" 
def a2(): 
    a = "x" * 2 
def a3(): 
    n = 2 
    a = "x" * n 


import dis 
print "a1:" 
dis.dis(a1) 

print "a2:" 
dis.dis(a2) 

print "a3:" 
dis.dis(a3) 

В CPython 2.6.4, эти выходы:

a1: 
    4   0 LOAD_CONST    1 ('x') 
       3 STORE_FAST    0 (a) 
       6 LOAD_CONST    0 (None) 
       9 RETURN_VALUE 
a2: 
    6   0 LOAD_CONST    3 ('xx') 
       3 STORE_FAST    0 (a) 
       6 LOAD_CONST    0 (None) 
       9 RETURN_VALUE 
a3: 
    8   0 LOAD_CONST    1 (2) 
       3 STORE_FAST    0 (n) 

    9   6 LOAD_CONST    2 ('x') 
       9 LOAD_FAST    0 (n) 
      12 BINARY_MULTIPLY 
      13 STORE_FAST    1 (a) 
      16 LOAD_CONST    0 (None) 
      19 RETURN_VALUE 

Наконец, обратите внимание, что вы можете сказать a = intern(a); b = intern(b) создать интернированы версии, если строки, которые гарантируют, что a is b верен. Если все, что вам нужно, это проверить равенство строк, просто используйте a == b.

+0

Упрощенный для полной полноты. – kindall

+0

О стажере, я нашел строки ниже или равны 20 char length автоматизированы с помощью Python. Мой настоящий вопрос: почему «x» * 2 (интернированный) отличается от «x» * n .. (не интернирован, даже если n <= 20) – pyrou

17

Оператор is говорит вам, является ли две переменные указывают на тот же объект в памяти. Это редко полезно и часто путают с оператором ==, который говорит вам, будут ли два объекта «выглядеть одинаково».

Это особенно сбивает с толку при использовании с вещами, такими как короткие строковые литералы, потому что компилятор Python ставит их для повышения эффективности. Другими словами, когда вы пишете "xx", компилятор (испускает байт-код) создает один строковый объект в памяти и вызывает все литералы "xx", чтобы указать на него. Это объясняет, почему ваши первые два сравнения являются True. Обратите внимание на то, что вы можете получить идентификатор строки, вызвав id на них, которые (по крайней мере, на CPython, вероятно) их адрес в памяти:

>>> a = "xx" 
>>> b = "xx" 
>>> id(a) 
38646080 
>>> id(b) 
38646080 
>>> a is b 
True 
>>> a = "x"*10000 
>>> b = "x"*10000 
>>> id(a) 
38938560 
>>> id(b) 
38993504 
>>> a is b 
False 

Третий том, что компилятор не интернированные строки a и b, по какой-либо причине (вероятно, потому, что он недостаточно умен, чтобы заметить, что переменная n определена один раз, а затем никогда не изменялась).

На самом деле вы можете заставить Python интернировать строки, ну, asking it to. Это даст вам возможность увеличить производительность и может помочь. Это бесполезно.

Мораль: не использовать is со строковыми литералами. Или int литералы. Или в любом месте, на самом деле это не значит.

+0

Было бы полезно добавить объяснение, почему работают первые два 'a is b'. –

+0

И если вы хотите продолжить проверку, проверьте результаты «id (a)» и «id (b)» для трех случаев. – user470379

+0

* по любой причине *. хорошо причина вполне очевидна, не так ли? потому что компилятор не создает байтовый код для этих n-длинных строк. – SilentGhost

Смежные вопросы