Почему ключевое слово «is» имеет другое поведение, когда в строке есть точка? - PullRequest
56 голосов
/ 18 мая 2010

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

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

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

Я только что узнал, что из-за некоторых сценариев фильтрации моего личного домена это не получилось.

Ответы [ 2 ]

90 голосов
/ 18 мая 2010

is проверяет идентичность объекта, и любая реализация Python, когда он встречает литерал неизменяемых типов, совершенно свободно либо создает новый объект этого неизменяемого типа, или искать среди существующих объектов этого типа, чтобы увидеть, могут ли некоторые из них быть использованы повторно (путем добавления новой ссылки на тот же базовый объект).Это прагматичный выбор оптимизации, и не подчиняется семантическим ограничениям, поэтому ваш код никогда не должен полагаться на то, по какому пути может идти реализация Give (или он может сломаться с выпуском исправления / оптимизации 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 ed в первый раз (как видите, возвращаемое значение intern - это тот же объект, что и x и y, поскольку он имеет тот же id()).Конечно, вам также не следует полагаться на это - оптимизатор не имеет для автоматического интернирования, это всего лишь эвристика оптимизации;если вам нужно intern ed строка, intern их явно, просто для безопасности.Когда вы явно делаете внутренние строки ...:

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

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

Edit : просто чтобы уточнить, вот некоторые различия в производительности, о которых я говорю, на медленном 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

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

15 голосов
/ 18 мая 2010

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