Чому порівняння рядків із використанням "==" або "is" іноді дає інший результат?


1146

У мене є програма Python, у якій дві змінні встановлено значення 'public'. У умовному виразі у мене є порівняння, var1 is var2яке не вдається, але якщо я var1 == var2його змінюю, воно повертається True.

Тепер, якщо я відкриваю інтерпретатор Python і роблю те саме "є" порівняння, це успішно.

>>> s1 = 'public'
>>> s2 = 'public'
>>> s2 is s1
True

Що я тут пропускаю?



3
Ця проблема також виникає , коли ви читаєте консольне введення з допомогою , наприклад: input = raw_input("Decide (y/n): "). У цьому випадку введіть "y" і if input == 'y':поверне "True", а if input is 'y':поверне False.
Semjon Mössinger

4
Цей блог дає набагато більш повне пояснення, ніж будь-яка відповідь guilload.com/python-string-interning
Chris_Rands

1
Як @ Кріс-ріко згадує, I велике пояснення тут stackoverflow.com/q/15541404/1695680
ThorSummoner

Відповіді:


1533

isє тестування ідентичності, тестування ==рівності. те, що відбувається у вашому коді, буде перераховано у перекладачі так:

>>> a = 'pub'
>>> b = ''.join(['p', 'u', 'b'])
>>> a == b
True
>>> a is b
False

так, недарма вони не однакові, правда?

Іншими словами: isєid(a) == id(b)


17
ах, як екв? проти рівних? за схемою, отримав це.
Джотос

47
Або ==vs .equals()на Java. Найкраще, що Python ==не є аналогом Java ==.
MatrixFrog

11
@ Крайст: існує лише одне Noneзначення. Отже, він завжди має однаковий ідентифікатор.
SilentGhost

18
Це не стосується прикладу "є -> Правда" ОП.
користувач2864740

6
@AlexanderSupertramp, через струнне інтернування .
Кріс Ріко

569

Інші відповіді тут правильні: isвикористовується для порівняння ідентичності , тоді ==як використовується для порівняння рівності . Оскільки вам важлива рівноправність (два рядки повинні містити однакові символи), в цьому випадку isоператор просто помиляється, і вам слід скористатися ==натомість.

Причина, що isпрацює в інтерактивному режимі, полягає в тому, що (більшість) рядкових літералів інтерновано за замовчуванням. З Вікіпедії:

Інтерновані рядки прискорюють порівняння рядків, які іноді є вузьким місцем у додатках (таких як компілятори та динамічні режими мови програмування), які значною мірою покладаються на хеш-таблиці з рядковими клавішами. Без інтернування, перевірка рівності двох різних рядків передбачає вивчення кожного символу обох рядків. Це повільно з кількох причин: це властиво O (n) довжиною струн; зазвичай потрібні читання з декількох областей пам'яті, які потребують часу; а читання заповнює кеш процесора, тобто менше кешу доступно для інших потреб. З інтернованими рядками достатньо простого тесту ідентичності об'єкта після початкової роботи інтерна; це зазвичай реалізується як тест рівності вказівника,

Отже, коли у вашій програмі є два рядкові літерали (слова, які буквально набрані у вихідний код програми, оточені лапками), які мають однакове значення, компілятор Python автоматично інтернує рядки, змушуючи їх зберігати однаково розташування пам'яті. (Зауважте, що це не завжди буває, і правила, коли це відбувається, є доволі суперечливими, тому, будь ласка, не покладайтеся на таку поведінку у виробничому коді!)

Оскільки у вашому інтерактивному сеансі обидва рядки фактично зберігаються в одному і тому ж місці пам'яті, вони мають однакову ідентичність , тому isоператор працює так, як очікувалося. Але якщо ви будуєте рядок яким-небудь іншим методом (навіть якщо цей рядок містить абсолютно однакові символи), то рядок може бути рівним , але це не одна і та ж строка - тобто вона має іншу ідентичність , тому що це зберігається в іншому місці в пам'яті.


6
Де хтось може прочитати більше про суперечливі правила, коли рядки інтерновані?
Noctis Skytower

88
+1 для ретельного пояснення. Не впевнений, як інша відповідь отримала стільки відгуків, не пояснюючи, що АКТУАЛЬНО сталося.
That1Guy

4
це саме те, про що я думав, коли читав питання. Прийнята відповідь коротка, але містить факт, але ця відповідь пояснює речі набагато краще. Приємно!
Snađошƒаӽ

3
@NoctisSkytower Гугл те саме і знайшов це guilload.com/python-string-interning
xtreak

5
@ naught101: Ні, правило полягає в тому, щоб обирати ==та isбазувати, який тип перевірки ви хочете. Якщо ви дбаєте про те, щоб рядки були рівними (тобто мали однаковий вміст), то завжди слід використовувати ==. Якщо ви переймаєтесь тим, чи відносяться два імена Python до одного і того ж екземпляра об'єкта, вам слід скористатися is. Можливо, вам знадобиться, isякщо ви пишете код, який обробляє безліч різних значень, не піклуючись про їх вміст, або якщо ви знаєте, що є щось одне, і ви хочете ігнорувати інші об'єкти, що претендують на цю річ. Якщо ви не впевнені, завжди вибирайте ==.
Даніель Приден

108

isКлючове слово тест ідентичності об'єкта в той час як ==порівняння значення.

Якщо ви використовуєте is, результат буде істинним тоді і лише тоді, коли об’єкт є тим самим об'єктом. Однак ==буде правдою будь-коли, коли значення об’єкта будуть однаковими.


57

Останнє, що потрібно зазначити, ви можете використовувати цю sys.internфункцію, щоб переконатися, що ви отримуєте посилання на ту саму рядок:

>>> from sys import intern
>>> a = intern('a')
>>> a2 = intern('a')
>>> a is a2
True

Як було зазначено вище, вам не слід використовувати isдля визначення рівності рядків. Але це може бути корисно знати, якщо у вас є якісь дивні вимоги до використання is.

Зауважте, що ця internфункція була вбудованою в Python 2, але була переміщена до sysмодуля в Python 3.


43

isє тестування ідентичності, тестування ==рівності. Це означає, що isце спосіб перевірити, чи є дві речі однаковими , або просто рівнозначними.

Скажіть, у вас простий personоб’єкт. Якщо його назвали "Джек" і йому "23" роки, це рівнозначно ще одному 23-річному Джеку, але це не та сама людина.

class Person(object):
   def __init__(self, name, age):
       self.name = name
       self.age = age

   def __eq__(self, other):
       return self.name == other.name and self.age == other.age

jack1 = Person('Jack', 23)
jack2 = Person('Jack', 23)

jack1 == jack2 #True
jack1 is jack2 #False

Вони одного віку, але вони не той самий екземпляр людини. Рядок може бути еквівалентний іншому, але це не той самий об'єкт.


Якщо ви зміните набір jack1.age = 99, це не зміниться jack2.age. Це тому, що це два різні екземпляри, так jack1 is not jack2. Однак вони можуть дорівнювати один одному, jack1 == jack2якщо ім’я та їхній вік однакові. Це стає складніше для рядків, оскільки рядки незмінні в Python, і Python часто повторно використовує один і той же екземпляр. Мені подобається це пояснення, оскільки воно використовує прості випадки (звичайний об'єкт), а не спеціальні випадки (рядки).
Flimm

37

Це бічна нота, але в ідіоматичному пітоні ви часто бачите такі речі, як:

if x is None: 
    # some clauses

Це безпечно, оскільки гарантовано є один екземпляр Null Object (тобто None) .


1
Чи те саме стосується істинного і хибного? Так відповідатиме лише один екземпляр?
HandyManDan

1
@HandyManDan Так, вони є однотонними і в python 2, і 3.
kamillitw

@kamillitw, але в Python 2 ви можете перепризначити False та True.
Martijn Pieters

28

Якщо ви не впевнені, що робите, скористайтеся '=='. Якщо у вас є трохи більше знань про це, ви можете використовувати "is" для відомих об'єктів типу "None".

Інакше вам стане цікаво, чому все не працює і чому це відбувається:

>>> a = 1
>>> b = 1
>>> b is a
True
>>> a = 6000
>>> b = 6000
>>> b is a
False

Я навіть не впевнений, якщо деякі речі гарантовано залишатимуться однаковими між різними версіями / реалізаціями python.


1
Цікавий приклад, який показує, як переназначення ints викликає цю умову. Чому це не вдалося? Це через стажування чи щось інше?
Пол

Схоже , що чому - то це повертає брехня може в зв'язку з реалізацією інтерпретатора: stackoverflow.com/questions/132988 / ...
Paul


@ArchitJain Так, ці посилання пояснюють це досить добре. Прочитавши їх, ви дізнаєтесь, якими цифрами ви можете скористатися "є". Я просто хочу, щоб вони пояснили, чому це все-таки не добре робити це :) Ти знаєш, що це не робить гарною ідеєю вважати, що всі інші так само роблять (або що інтерналізований діапазон номерів ніколи не зміниться)
Маттіас Нілссон

20

З мого обмеженого досвіду роботи з python, isвикористовується для порівняння двох об'єктів, щоб побачити, чи є вони одним і тим же об'єктом на відміну від двох різних об'єктів з однаковим значенням. ==використовується для визначення того, чи є значення однакові.

Ось хороший приклад:

>>> s1 = u'public'
>>> s2 = 'public'
>>> s1 is s2
False
>>> s1 == s2
True

s1є рядком unicode і s2є звичайним рядком. Вони не одного типу, але мають однакове значення.


17

Я думаю, що це пов'язано з тим, що, коли порівняння 'є', порівняння оцінюється на хибне, використовуються два різних об'єкта. Якщо він оцінюється як істинний, це означає, що внутрішньо він використовує той самий точний об'єкт, а не створює новий, можливо, тому, що ви створили їх протягом частки 2 або більше секунд і через те, що між оптимізованими та використовує той самий об’єкт.

Ось чому вам слід використовувати оператор рівності ==, а не isдля порівняння значення об’єкта рядка.

>>> s = 'one'
>>> s2 = 'two'
>>> s is s2
False
>>> s2 = s2.replace('two', 'one')
>>> s2
'one'
>>> s2 is s
False
>>> 

У цьому прикладі я створив s2, який був іншим рядковим об'єктом, раніше рівним 'one', але це не той самий об'єкт, як s, оскільки інтерпретатор не використовував той самий об'єкт, як я не спочатку призначив його "one", якби я це робив, це зробило б їх таким самим об'єктом.


3
Використання .replace()в якості прикладу в цьому контексті, мабуть, не найкраще, тому що його семантика може бути заплутаною. s2 = s2.replace()буде завжди створювати новий об'єкт рядки, призначити новий об'єкт строкового s2, а потім позбутися від строкового об'єкта , який s2використовується для вказівки. Тож навіть якщо ви це зробили, s = s.replace('one', 'one')ви все одно отримаєте новий об'єкт рядка.
Даніель Приден

13

Я вважаю, що це відоме як "інтерновані" струни. Python робить це, як і Java, а також C і C ++ при компілюванні в оптимізованих режимах.

Якщо ви використовуєте два однакові рядки, замість того, щоб витрачати пам'ять, створюючи два рядкові об'єкти, всі інтерновані рядки з однаковим вмістом вказують на одну пам'ять.

Це призводить до того, що оператор Python "є" повертає True, оскільки два рядки з однаковим вмістом вказують на один і той же рядковий об'єкт. Це станеться і на Яві, і на C.

Це корисно лише для економії пам'яті. Ви не можете покластися на нього для перевірки рівності рядків, тому що різні інтерпретатори та компілятори та двигуни JIT не завжди можуть це зробити.


12

Я відповідаю на це питання, хоч питання старе, тому що жодна відповідь вище не цитує посилання на мову

Насправді оператор перевіряє особистість і == оператор перевіряє рівність,

З мовної довідки:

Типи впливають майже на всі аспекти поведінки об'єкта. Навіть на важливість ідентичності об'єкта в деякому сенсі впливає: для незмінних типів операції, що обчислюють нові значення, можуть фактично повернути посилання на будь-який існуючий об'єкт одного типу та значення, тоді як для об'єктів, що змінюються, це заборонено . Наприклад, після a = 1; b = 1, a і b можуть або не можуть посилатися на один і той же об'єкт зі значенням один, залежно від реалізації, але після c = []; d = [], c і d гарантовано посилаються на два різні, унікальні, новостворені порожні списки. (Зверніть увагу, що c = d = [] призначає один і той же об'єкт і c, і d.)

тому з наведеного вище твердження ми можемо зробити висновок про те, що рядки, які є незмінним типом, можуть не працювати при позначенні "є" і можуть бути успішними, якщо встановлено прапорець "є"

Це ж стосується int, кортеж яких також є незмінними типами


8

==Тестове значення оператора еквівалентність. isІдентичність тести оператора об'єкта, тести Python чи два дійсно той самий об'єкт (тобто, жити за тією ж адресою в пам'яті).

>>> a = 'banana'
>>> b = 'banana'
>>> a is b 
True

У цьому прикладі Python створив лише один об'єкт рядка, і обидва, aі bпосилається на нього. Причина полягає в тому, що Python внутрішньо кешує та повторно використовує деякі рядки як оптимізацію, в пам’яті дійсно є лише рядок «банан», який поділяється a і b; Щоб запустити нормальну поведінку, потрібно використовувати довші рядки:

>>> a = 'a longer banana'
>>> b = 'a longer banana'
>>> a == b, a is b
(True, False)

Створюючи два списки, ви отримуєте два об'єкти:

>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> a is b
False

У цьому випадку ми б сказали, що два списки є рівнозначними, оскільки вони мають однакові елементи, але не тотожні, оскільки вони не є одним і тим же об'єктом. Якщо два об'єкти однакові, вони також є рівнозначними, але якщо вони рівнозначні, вони не обов'язково однакові.

Якщо aпосилається на об'єкт і ви призначаєте b = a, то обидві змінні посилаються на один і той же об'єкт:

>>> a = [1, 2, 3]
>>> b = a
>>> b is a
True

7

isбуде порівняти місце пам'яті. Він використовується для порівняння об'єктного рівня.

==порівняємо змінні в програмі. Він використовується для перевірки на рівні значень.

is перевірки на відповідність рівня адреси

== перевіряє еквівалентність рівня цінності


3

isє тестування ідентичності, тестування ==рівності (див. Документація Python ).

У більшості випадків, якщо a is b, то a == b. Але є винятки, наприклад:

>>> nan = float('nan')
>>> nan is nan
True
>>> nan == nan
False

Отже, ви можете використовувати лише isдля тестів на ідентичність, а не на тести рівності.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.