Подтвердить что ты не робот

Почему ключевое слово "есть" имеет другое поведение, когда в строке есть точка?

Рассмотрим этот код:

>>> x = "google"
>>> x is "google"
True
>>> x = "google.com"
>>> x is "google.com"
False
>>>

Почему так?

Чтобы убедиться, что это правильно, я только что тестировал на Python 2.5.4, 2.6.5, 2.7b2, Python 3.1 на Windows и Python 2.7b1 на Linux.

Похоже, что существует согласованность во всех них, поэтому он по дизайну. Я что-то пропустил?

Я просто выясню, что из-за моей личной фильтрации домена script не удалось.

4b9b3361

Ответ 1

is проверяет идентичность объекта, и любая реализация Python, когда она встречает литерал неизменяемых типов, совершенно свободна либо создавать новый объект этого неизменяемого типа, либо искать через существующие объекты этого типа, чтобы увидеть, из них можно было бы повторно использовать (путем добавления новой ссылки на один и тот же базовый объект). Это прагматичный выбор оптимизации и не с учетом семантических ограничений, поэтому ваш код никогда не должен полагаться на какой путь может дать реализация реализации (или он может сломаться с выпуском исправления/оптимизации Python!).

Рассмотрим, например:

>>> import dis
>>> def f():
...   x = 'google.com'
...   return x is 'google.com'
... 
>>> dis.dis(f)
  2           0 LOAD_CONST               1 ('google.com')
              3 STORE_FAST               0 (x)

  3           6 LOAD_FAST                0 (x)
              9 LOAD_CONST               1 ('google.com')
             12 COMPARE_OP               8 (is)
             15 RETURN_VALUE    

поэтому в этой конкретной реализации внутри функции ваше наблюдение не применяется, и только один объект создается для литерала (любого литерала) и, действительно:

>>> f()
True

Прагматично, потому что внутри функции, выполняющей проход через локальную таблицу констант (чтобы сохранить некоторую память, не создавая несколько постоянных неизменяемых объектов там, где их хватает), довольно дешево и быстро и может предлагать хорошие показатели производительности, поскольку функция может после этого повторно называться.

Но, та же самая реализация в интерактивном приглашении ( Изменить: я изначально думал, что это также произойдет на верхнем уровне модуля, но комментарий @Thomas установил меня правильно, см. позже)

>>> x = 'google.com'
>>> y = 'google.com'
>>> id(x), id(y)
(4213000, 4290864)

НЕ ЗАПРЕЩАЕТСЯ пытаться сохранить память таким образом - id отличаются друг от друга, т.е. разные объекты. Существуют потенциально более высокие затраты и более низкая доходность, и поэтому эвристика этого оптимизатора реализации говорит, что он не ищет поиска и просто идет вперед.

Изменить: на верхнем уровне модуля, за наблюдение @Thomas, учитывая, например:

$ cat aaa.py
x = 'google.com'
y = 'google.com'
print id(x), id(y)

снова мы видим оптимизацию памяти на основе таблиц на основе констант в этой реализации:

>>> import aaa
4291104 4291104

(конец редактирования для наблюдения @Thomas).

Наконец, снова в той же реализации:

>>> x = 'google'
>>> y = 'google'
>>> id(x), id(y)
(2484672, 2484672)

эвристика здесь различна, потому что буквальная строка "выглядит так, как будто она может быть идентификатором" - поэтому ее можно использовать в операции, требующей интернирования... поэтому оптимизатор ставит ее в любом случае (и после интернирования, ища это становится очень быстро, конечно). И действительно, неожиданное удивление...:

>>> z = intern(x)
>>> id(z)
2484672

... x имеет был intern в первый раз (как вы видите, возвращаемое значение intern - это тот же объект, что и x и y, так как он имеет тот же id()). Конечно, вы тоже не должны полагаться на это: оптимизатор не имеет, чтобы ставить что-то автоматически, это просто оптимизация эвристики; если вам нужна строка intern ed, intern явно, просто чтобы быть в безопасности. Когда вы сделаете строки-помещики явно...:

>>> x = intern('google.com')
>>> y = intern('google.com')
>>> id(x), id(y)
(4213000, 4213000)

..., тогда вы do гарантируете точно тот же объект (т.е. тот же самый id()) каждый раз - так что вы можете применять микрооптимизации, такие как проверка с помощью is а не == (я почти никогда не обнаружил, что незначительное увеличение производительности стоит того, чтобы беспокоиться;).

Изменить: просто для того, чтобы уточнить, вот о тех различиях в производительности, о которых я говорю, на медленном Macbook Air...:

$ python -mtimeit -s"a='google';b='google'" 'a==b'
10000000 loops, best of 3: 0.132 usec per loop
$ python -mtimeit -s"a='google';b='google'" 'a is b'
10000000 loops, best of 3: 0.107 usec per loop
$ python -mtimeit -s"a='goo.gle';b='goo.gle'" 'a==b'
10000000 loops, best of 3: 0.132 usec per loop
$ python -mtimeit -s"a='google';b='google'" 'a is b'
10000000 loops, best of 3: 0.106 usec per loop
$ python -mtimeit -s"a=intern('goo.gle');b=intern('goo.gle')" 'a is b'
10000000 loops, best of 3: 0.0966 usec per loop
$ python -mtimeit -s"a=intern('goo.gle');b=intern('goo.gle')" 'a == b'
10000000 loops, best of 3: 0.126 usec per loop

... несколько десятков наносекунд в любом случае, самое большее. Таким образом, стоит даже подумать только о самых экстремальных "оптимизируйте [исключенную исключение] из этого [ругательного удаления] узких мест в производительности" -)

Ответ 2

"is" является тестом идентификации. Python имеет некоторое поведение кэширования для небольших целых чисел и (по-видимому) строк. "is" лучше всего использовать для одноэлементного тестирования (например, None).

>>> x = "google"
>>> x is "google"
True
>>> id(x)
32553984L
>>> id("google")
32553984L
>>> x = "google.com"
>>> x is "google.com"
False
>>> id(x)
32649320L
>>> id("google.com")
37787888L