Оператор is ведет себя по-разному при сравнении строк с пробелами
Я начал изучать Python (Python 3.3), и я пытался is
оператор. Я попробовал это:
>>> b = 'is it the space?'
>>> a = 'is it the space?'
>>> a is b
False
>>> c = 'isitthespace'
>>> d = 'isitthespace'
>>> c is d
True
>>> e = 'isitthespace?'
>>> f = 'isitthespace?'
>>> e is f
False
Кажется, что пространство и знак вопроса делают is
вести себя по-другому. В чем дело?
РЕДАКТИРОВАТЬ: я знаю, что я должен использовать ==
Я просто хотел знать почему is
ведет себя так
6 ответов
Предупреждение: этот ответ о деталях реализации конкретного интерпретатора Python. сравнивая строки с is
== плохая идея.
Ну, по крайней мере, для cpython3.4/2.7.3 ответ "нет, это не пробел". Не только пробелы:
Два строковых литерала будут совместно использовать память, если они являются буквенно-цифровыми или находятся в одном и том же блоке (файл, функция, класс или одна команда интерпретатора)
Выражение, которое оценивается как строка, приведет к тому, что объект будет идентичен объекту, созданному с использованием строкового литерала, если и только если он создан с использованием констант и двоичных / унарных операторов, а результирующая строка будет короче 21 символа.
Отдельные персонажи уникальны.
Примеры
Буквенно-цифровые строковые литералы всегда совместно используют память:
>>> x='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
>>> y='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
>>> x is y
True
Не буквенно-цифровые строковые литералы разделяют память тогда и только тогда, когда они разделяют включающий синтаксический блок:
(Интерпретатор)
>>> x='`!@#$%^&*() \][=-. >:"?<a'; y='`!@#$%^&*() \][=-. >:"?<a';
>>> z='`!@#$%^&*() \][=-. >:"?<a';
>>> x is y
True
>>> x is z
False
(файл)
x='`!@#$%^&*() \][=-. >:"?<a';
y='`!@#$%^&*() \][=-. >:"?<a';
z=(lambda : '`!@#$%^&*() \][=-. >:"?<a')()
print(x is y)
print(x is z)
Выход: True
а также False
Для простых двоичных операций компилятор выполняет очень простое распространение констант (см. Peephole.c), но со строками это делает только в том случае, если результирующая строка короче 21 символа. Если это так, действуют правила, упомянутые ранее:
>>> 'a'*10+'a'*10 is 'a'*20
True
>>> 'a'*21 is 'a'*21
False
>>> 'aaaaaaaaaaaaaaaaaaaaa' is 'aaaaaaaa' + 'aaaaaaaaaaaaa'
False
>>> t=2; 'a'*t is 'aa'
False
>>> 'a'.__add__('a') is 'aa'
False
>>> x='a' ; x+='a'; x is 'aa'
False
Отдельные символы всегда разделяют память, конечно:
>>> chr(0x20) is ' '
True
Чтобы немного расширить ответ Игнасио: is
Оператор - это тождественный оператор. Используется для сравнения идентичности объекта. Если вы создаете два объекта с одинаковым содержимым, то, как правило, это не тот случай, когда идентичность объекта дает значение true. Это работает для некоторых небольших строк, потому что CPython, эталонная реализация Python, хранит содержимое отдельно, делая все эти объекты ссылками на одно и то же строковое содержимое. Итак is
Оператор возвращает true для тех.
Это, однако, является деталью реализации CPython и обычно не гарантируется ни для CPython, ни для какой-либо другой реализации. Так что использовать этот факт - плохая идея, так как он может сломать любой другой день.
Для сравнения строк вы используете ==
оператор, который сравнивает равенство объектов. Два строковых объекта считаются равными, если они содержат одинаковые символы. Так что это правильный оператор для сравнения строк и is
следует вообще избегать, если вы явно не хотите идентифицировать объект (пример: a is False
).
Если вы действительно заинтересованы в деталях, вы можете найти реализацию строк CPython здесь. Но опять же: это детали реализации, поэтому вам никогда не следует требовать, чтобы это работало.
is
оператор опирается на id
функция, которая guaranteed to be unique among simultaneously existing objects.
В частности, id
возвращает адрес памяти объекта. Похоже, что CPython имеет согласованные адреса памяти для строк, содержащих только символы az и AZ.
Однако, похоже, это имеет место только в том случае, если строка была назначена переменной:
Здесь идентификатор "foo" и идентификатор a
подобные. a
перед проверкой идентификатора было установлено значение "foo".
>>> a = "foo"
>>> id(a)
4322269384
>>> id("foo")
4322269384
Тем не менее, идентификатор "бар" и идентификатор " a
различаются при проверке идентификатора "bar" перед установкой a
равно "бар".
>>> id("bar")
4322269224
>>> a = "bar"
>>> id(a)
4322268984
Проверка идентификатора "bar" снова после установки a
равно "bar" возвращает тот же идентификатор.
>>> id("bar")
4322268984
Таким образом, кажется, что cPython сохраняет согласованные адреса памяти для строк, содержащих только a-zA-Z, когда эти строки назначены переменной. Также вполне возможно, что это зависит от версии: я запускаю Python 2.7.3 на MacBook. Другие могут получить совершенно другие результаты.
Фактически ваш код сводится к сравнению идентификаторов объектов (т.е. их физических адресов). Так что вместо твоего это сравнение:
>>> b = 'is it the space?'
>>> a = 'is it the space?'
>>> a is b
False
Ты можешь сделать:
>>> id(a) == id(b)
False
Но обратите внимание, что если бы a и b были непосредственно в сравнении, это сработало бы.
>>> id('is it the space?') == id('is it the space?')
True
Фактически, в выражении есть разделение между одинаковыми статическими строками. Но в масштабе программы есть только разделение для словоподобных строк (так что ни пробелов, ни знаков пунктуации).
Вы не должны полагаться на это поведение, так как оно нигде не задокументировано и является частью реализации.
Две или более идентичных строк последовательных буквенно-цифровых (только) символов хранятся в одной структуре, поэтому они имеют общую ссылку на память. С 1990-х годов в Интернете есть сообщения об этом явлении. Очевидно, так было всегда. Я никогда не видел разумных предположений относительно того, почему это так. Я знаю только, что это так. Кроме того, если вы разделяете и повторно соединяете буквенно-цифровые строки, чтобы удалить пробелы между словами, полученные идентичные буквенно-цифровые строки НЕ имеют общей ссылки, что я нахожу странным. См. ниже:
Добавьте любое небуквенно-цифровое значение одинаково к обеим строкам, и они мгновенно станут копиями, но не общими ссылками.
a ="abbacca"; b = "abbacca"; a is b => True
a ="abbacca "; b = "abbacca "; a is b => False
a ="abbacca?"; b = "abbacca?"; a is b => False
~ Д-р. С.
Оператор 'is' сравнивает реальный объект.
c is d
также должен быть ложным. Я предполагаю, что Python делает некоторую оптимизацию, и в этом случае это тот же объект.