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
ed в самый первый раз (как видите, возвращаемое значение intern
является тем же объектом, что и x
и y
, так как он имеет тот же id()
). Конечно, вы также не должны полагаться на это - оптимизатор не должен интернировать что-либо автоматически, это просто эвристика оптимизации; если вам нужна строка intern
ed, intern
укажите их явно, на всякий случай. Когда вы выполняете внутренние строки явно...:
>>> x = intern('google.com')
>>> y = intern('google.com')
>>> id(x), id(y)
(4213000, 4213000)
...затем вы делаете гарантируете, что один и тот же объект (т.е. одно и то же 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
...не более нескольких десятков наносекунд в любом случае. Так что стоит даже задуматься только в самых экстремальных ситуациях «оптимизировать [ругательство удалено] из этого [ругательство удалено] узкого места производительности»!-)
person
Alex Martelli
schedule
18.05.2010
False
в Python 2.5.2. - person Håvard S   schedule 18.05.2010