Translate How not to use is operator example

This commit is contained in:
Vadim Nifadev 2024-04-18 15:08:23 +03:00
parent e2d6ee66cc
commit a7985b9edc
1 changed files with 125 additions and 0 deletions

View File

@ -331,3 +331,128 @@ False
В итоге, `1 < 1` выполняется и дает результат `False`
---
### ▶ Как не надо использовать оператор `is`
<!-- Example ID: 230fa2ac-ab36-4ad1-b675-5f5a1c1a6217 --->
Ниже приведен очень известный пример.
1\.
```py
>>> a = 256
>>> b = 256
>>> a is b
True
>>> a = 257
>>> b = 257
>>> a is b
False
```
2\.
```py
>>> a = []
>>> b = []
>>> a is b
False
>>> a = tuple()
>>> b = tuple()
>>> a is b
True
```
3\.
**Результат**
```py
>>> a, b = 257, 257
>>> a is b
True
```
**Вывод (только для Python 3.7.x)**
```py
>>> a, b = 257, 257
>>> a is b
False
```
#### 💡 Объяснение:
**Разница между `is` и `==`**.
* Оператор `is` проверяет, ссылаются ли оба операнда на один и тот же объект (т.е. проверяет, совпадают ли идентификаторы операндов или нет).
* Оператор `==` сравнивает значения обоих операндов и проверяет, одинаковы ли они.
* Таким образом, оператор `is` предназначен для равенства ссылок, а `==` - для равенства значений. Пример, чтобы прояснить ситуацию,
```py
>>> class A: pass
>>> A() is A() # 2 пустых объекта в разных ячейках памяти
False
```
**`256` - существующий объект, а `257` - нет**.
При запуске python числа от `-5` до `256` записываются в память. Эти числа используются часто, поэтому имеет смысл просто иметь их наготове.
Перевод цитаты из [документации](https://docs.python.org/3/c-api/long.html)
> Текущая реализация хранит массив целочисленных объектов для всех целых чисел от -5 до 256, когда вы создаете int в этом диапазоне, вы просто получаете обратно ссылку на существующий объект.
```py
>>> id(256)
10922528
>>> a = 256
>>> b = 256
>>> id(a)
10922528
>>> id(b)
10922528
>>> id(257)
140084850247312
>>> x = 257
>>> y = 257
>>> id(x)
140084850247440
>>> id(y)
140084850247344
```
Интерпретатор не понимает, что до выполнения выражения `y = 257` целое число со значением `257` уже создано, и поэтому он продолжает создавать другой объект в памяти.
Подобная оптимизация применима и к другим **изменяемым** объектам, таким как пустые кортежи. Поскольку списки являются изменяемыми, поэтому `[] is []` вернет `False`, а `() is ()` вернет `True`. Это объясняет наш второй фрагмент. Перейдем к третьему,
**И `a`, и `b` ссылаются на один и тот же объект при инициализации одним и тем же значением в одной и той же строкеi**.
**Вывод**
```py
>>> a, b = 257, 257
>>> id(a)
140640774013296
>>> id(b)
140640774013296
>>> a = 257
>>> b = 257
>>> id(a)
140640774013392
>>> id(b)
140640774013488
```
* Когда a и b инициализируются со значением `257` в одной строке, интерпретатор Python создает новый объект, а затем одновременно ссылается на него во второй переменной. Если делать это в отдельных строках, интерпретатор не "знает", что объект `257` уже существует.
* Это оптимизация компилятора и относится именно к интерактивной среде. Когда вы вводите две строки в интерпретаторе, они компилируются отдельно, поэтому оптимизируются отдельно. Если выполнить этот пример в файле `.py', поведение будет отличаться, потому что файл компилируется весь сразу. Эта оптимизация не ограничивается целыми числами, она работает и для других неизменяемых типов данных, таких как строки (проверьте пример "Строки - это сложно") и плавающие числа,
```py
>>> a, b = 257.0, 257.0
>>> a is b
True
```
* Почему это не сработало в Python 3.7? Абстрактная причина в том, что такие оптимизации компилятора зависят от реализации (т.е. могут меняться в зависимости от версии, ОС и т.д.). Я все еще выясняю, какое именно изменение реализации вызвало проблему, вы можете проверить этот [issue](https://github.com/satwikkansal/wtfpython/issues/100) для получения обновлений.
---