Странный результат в питоне - PullRequest
7 голосов
/ 04 ноября 2010

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

>>> 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... ?

Ответы [ 2 ]

18 голосов
/ 04 ноября 2010

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

Это особенно запутанно, когда используется с такими вещами, как короткие строковые литералы, потому что интерны компилятора Pythonэто для эффективности.Другими словами, когда вы пишете "xx", компилятор (испускает байт-код, который) создает один строковый объект в памяти и заставляет все литералы "xx" указывать на него.Это объясняет, почему ваши первые два сравнения верны.Обратите внимание, что вы можете получить идентификатор строк, вызвав для них 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 интернировать строки, ну, в общем, с запросом .Это даст вам небольшое увеличение производительности и может помочь.Это, вероятно, бесполезно.

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

12 голосов
/ 04 ноября 2010

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...