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


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

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

2 ответа:

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

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

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

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

не потрудитесь попытаться сохранить память таким образом -ids-это разные, т. е. различные объекты. Существуют потенциально более высокие затраты и более низкие доходы, и поэтому эвристика оптимизатора этой реализации говорит ему не беспокоиться о поиске и просто идти вперед.

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

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

...тогда ты do обеспечить точно такой же объект (т. е., тот же 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

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

" is " - это проверка личности. Python имеет некоторое поведение кэширования для небольших целых чисел и (по-видимому) строк. "is" лучше всего использовать для одноэлементного тестирования (ex. 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