Рубрика: Компьютеры

Python: is. Равенство и эквивалентность

is or == picture

Новички часто путаются в конструкциях is и ==. Давайте разберемся, что к чему.

Сразу к сути: == (и его антагонист !=) применяются для проверки равенства (неравенства) значения двух объектов. Значение, это непосредственно то, что лежит в переменной. Значение числа 323235 – собственно число 323235. Тавтология. Но на примерах станет яснее.

Оператор is (и его антагонист is not) применяются проверки равенства (неравенства) ссылок на объект. Сразу отметим то, что на значение (допустим 323235) может быть копировано и храниться в разных местах (в разных объектах в памяти).

>> x = 323235
>> y = 323235
>> x == y
True
>> x is y
False

Видите, значение переменных равны по значению, но они ссылаются на разные объекты. Я не случайно взял большое число 323235. Дело в том, что в целях оптимизации интерпретатор Python при старте создает некоторые количество часто-используемых констант (от -5 до 256 включительно).

Следите внимательно за ловкостью рук:

>>> x = 256
>>> y = 256
>>> x is y
True
>>> x = 257
>>> y = 257
>>> x is y
False
>>> x = -5
>>> y = -5
>>> x is y
True
>>> x = -6
>>> y = -6
>>> x is y
False 

Поэтому новички часто совершают ошибку, считая, что писать == – это как-то не Python-way, а is – Python-way. Это ошибочное предположение может быть раскрыто не сразу.

Python старается кэшировать и переиспользовать строковые значения. Поэтому весьма вероятно, что переменные, содержащие одинаковые строки, будут содержать ссылки на одинаковые объекты. Но это не факт! Смотрите последний пример:

>>> x = "hello"
>>> y = "hello"
>>> x is y
True
>>> x = "hel" + "lo"
>>> y = "hello"
>>> x is y
True
>>> a = "hel"
>>> b = "lo"
>>> x = a + b
>>> y = "hello"
>>> x == y
True
>>> x is y
False

Мы составили строку из двух частей и она попала в другой объект. Python не догадался (и правильно) поискать ее в существующих строках.

Суть is (id)

В Python есть встроенная функция id. Она возвращает идентификатор объекта – некоторое число. Гарантируется, что оно будет различно для различных объектах в пределах одного интерпретатора. В реализации CPython – это просто адрес объекта в памяти интерпретатора.

Так вот:

a is b

Это тоже самое, что:

id(a) == id(b)

И все! Пример для проверки:

>>> x = 10.40
>>> y = 10.40
>>> x is y
False
>>> x == y
True

>>> id(x)
4453475504
>>> id(y)
4453475600
>>> id(x) == id(y)
False

>>> x = y
>>> x is y
True
>>> id(x)
4453475600
>>> id(y)
4453475600

Значения переменных равны, но их id – разные, и is выдает False. Как только мы к x привязали y, то ссылки стали совпадать.

Для чего можно применять is?

Если мы точно знаем уверены, что хотим проверять именно равенство ссылок на объекты (один ли это объект в памяти или разные).

Еще можно применять is для сравнения с None. None – это встроенная константа и двух None быть не может.

>>> x is None
False
>>> x = None
>>> x is None
True

Также для Ellipsis:

>>> ... is Ellipsis
True
>>> x = ...
>>> y = ...
>>> x is y
True

Я не рекомендую применять is для True и False.

Потому что короче писать if x:, чем if x is True:.

Можно применять is для сравнения типов с осторожностью (без учета наследования, т. е. проверка на точное совпадение типов):

>>> x = 10.5
>>> type(x) is float
True

С наследованием может быть конфуз:

>>> class Foo: ...
...
>>> class Bar(Foo): ...
...
>>> f = Foo()
>>> b = Bar()
>>> type(f) is Foo
True
>>> type(b) is Bar
True
>>> type(b) is Foo
False
>>> isinstance(b, Foo)
True

Не смотря на то, что Bar – наследник Foo, типы переменных foo и bar не совпадают. Если нам важно учесть наcледование, то пишите isinstance.

Нюанс: is not против is (not)

Важно знать, что is not – это один целый оператор, аналогичный id(x) != id(y). А в конструкции x is (not y) – у нас сначала будет логическое отрицание y, а потом просто оператор is.

Пример уловки:

>>> x = 10
>>> x is not None
True
>>> x is (not None)
False

Сравнение пользовательских классов

Далее речь пойдет об обычных == и !=. Можно определить магический метод __eq__, который обеспечит поведение при сравнении классов. Если он не реализован, то объекты будет сравниваться по ссылкам (как при is).

>>> class Baz: ...
...
>>> x = Baz()
>>> y = Baz()
>>> x == y
False
>>> x = y
>>> x == y
True

Если он реализован, то будет вызван метод __eq__ для левого операнда.

class Foo:
 def __init__(self, x):
  self.x = x
 def __eq__(self, other):
  print('Foo __eq__ {} and {}'.format(self, other))
  return self.x == other.x

>>> x = Foo(5)
>>> y = Foo(5)
>>> x == y
Foo __eq__ <__main__.Foo object at 0x109e9c048> and <__main__.Foo object at 0x109e8a5c0>
True

Метод __ne__ отвечает за реализацию !=. По умолчанию он вызывает not x.__eq__(y). Но рекомендуется реализовывать их оба вручную, чтобы поведение сравнения было согласовано и явно.

Вопрос к размышлению: что будет если мы сравним объекты разных классов, причем оба класса реализуют __eq__?

Что будет, если мы реализуем __ne__, но не реализуем __eq__?

А еще есть метод __cmp__. Это уже выходит за рамки статьи про is. Почитайте самостоятельно…

Специально для канала @pyway.

Не унывать

Мысли для себя: главное не позволять себе унывать. Погрустить немного и потюленничать в кроватке с планшетом иногда не так плохо, но у нас же есть планы и много работы, ожидающей, чтобы ее сделали… Если дома скучно, то хватаем ноутбук и бегом, например, в Макдональд. Можно, в коворкинг. Да, в следующий раз пойду исследовать коворкинги. Парадокс, но музыка и куча болтающих людей вокруг в данной ситуации совсем не мешают. Я же работал раньше в опен-спейсе, привык видимо. В поезде вот так не получается. Обычно там болтают не все, а лишь некоторые и нет музыки, заглушающей двух их трескотни. А когда множество людей треплется и весь этот гомон смешивается с музыкой, то эффект выходит даже положительным. Все-таки человеку, даже такому отъявленному интроверту, как я, нужно людское общество.

Взлом игры «Потребительская корзина» от РокетБанка

РокетБанк устроил конкурс с розыгрышем iPhone, хамона и макарон. В одном из заданий нужно играть в игру типа «Ну, погоди!». Несколько раз пробовал, но она оказалась для меня слишком сложной, поэтому я взломал движок Construct 2 (на котором она сделана) и прошел ее на раз-два.

Итак, нам понадобится браузер Chrome.

Открываем игру.

Кликаем правой кнопкой около игровой приставки, выбираем Inspect. (Картинки можно открыть пошире)

screen-shot-2016-09-29-at-23-14-55

В появившемся окне кликаем на Sources (наверху).

screen-shot-2016-09-29-at-23-15-03

Кликаем на три точки, затем Go to file.

screen-shot-2016-09-29-at-23-15-21

Находим файл c2runtime.js, переходим к нему (это движок игры).

screen-shot-2016-09-29-at-23-15-33

Нажимаем (Ctrl+F или Cmd+F на маке), это поиск. Находим нужную ф-цию (AddVar).

screen-shot-2016-09-29-at-23-15-51Дописываем туда код, как показано на рисунке.

Код смотрит, если переменная – очки за игру, то добавляем сразу по 10:

if(v.name == 'Score') x = 10;

screen-shot-2016-09-29-at-23-16-33

Нажимаем Ctrl+S (или Cmd+S на маке). Chrome может подвиснуть на пару секунд, не пугайтесь. Затем возвращаемся к игре, нажимаем «Заново» и «Играть». Теперь каждый собранный продукт дает нам по 10 очков вместо 1 очка. Нам остается набрать 100 и более очков, продуть, и все! Печать наша!

screen-shot-2016-09-29-at-23-19-31

Спасибо за внимание!

P. S. Оформляйте карты РокетБанка через мой реферал: https://rocketbank.ru/loves/maksim.koltsov

Вибробраслет

Ни компьютером единым. Можно еще и хобби иметь. Электроника ведь подойдет?

Вибробраслет почти готов! Осталось, как всегда, дело за малым: написать нормальный софт. Суть такова: на вас надет браслет, ответная часть — в USB компа. Когда происходит событие (емайл, напоминание, огонь под ногами в WoW), браслет вибрирует. #недождалсяэпплвотч

2

Художественное фото рабочего места.

3