Урок по треку Python 7 мин

Типы, изменяемость и сравнение в Python за 7 минут

Встроенные типы, изменяемые и неизменяемые объекты, is и ==, ссылки, копирование и истинность.

В Python всё объект: число, строка, функция, класс, модуль. У каждого объекта есть идентичность (id), тип (type) и значение. Переменная не хранит объект, а только ссылается на него. Почти все вопросы первой части собеса крутятся вокруг этой мысли.

Переменная это имя, а не коробка

a = [1, 2]
b = a          # второе имя для того же списка
b.append(3)
print(a)       # [1, 2, 3]

Присваивание ничего не копирует, оно привязывает имя к объекту. Поэтому b.append видно через a. А вот b = b + [4] создаст новый список и перепривяжет только b.

Встроенные типы

НеизменяемыеИзменяемые
int, float, complex, boollist
str, bytesdict
tuple, frozensetset, bytearray
None (единственный объект NoneType)пользовательские классы (по умолчанию)

int в Python неограниченной длины: 2 ** 100 считается точно. bool это подкласс int, поэтому True + True == 2. float это IEEE 754 двойной точности, отсюда 0.1 + 0.2 != 0.3. Для денег берут decimal.Decimal или считают в копейках целыми.

Неизменяемость не значит «нельзя поменять переменную»

Неизменяемый объект нельзя поменять на месте. Любая «модификация» строки создаёт новую строку:

s = 'кот'
t = s
s += 'ы'       # новый объект, t по-прежнему 'кот'

С изменяемыми += работает иначе: для списка это extend на месте.

x = [1]
y = x
x += [2]       # изменили тот же список
print(y)       # [1, 2]

Кортеж неизменяем, но может содержать изменяемые элементы: t = ([1], 2), и t[0].append(5) сработает. Такой кортеж нельзя положить в set или сделать ключом dict, потому что он не хешируемый.

is против ==

  • == сравнивает значения (вызывает __eq__).
  • is проверяет, что это один и тот же объект (одинаковый id).

is используют для синглтонов: x is None, x is True (редко), sentinel. Для чисел и строк is даёт непредсказуемый результат: CPython кеширует малые целые от -5 до 256 и интернирует часть строк, но это деталь реализации, а не гарантия языка.

a = 256; b = 256
a is b         # True в CPython, но полагаться нельзя
x = [1]; y = [1]
x == y, x is y # (True, False)

Истинность

Ложными считаются: None, False, ноль любого числового типа, пустые '', [], (), {}, set(), range(0). Всё остальное истинно, включая '0', 'False' и [0]. Свой класс управляет этим через __bool__ или __len__.

and и or возвращают не bool, а один из операндов: '' or 'гость' даст 'гость', 0 and 1/0 даст 0 и деление не выполнится.

Копирование

import copy
src = [[1, 2], [3]]
shallow = src.copy()       # или list(src), src[:]
deep = copy.deepcopy(src)
src[0].append(9)
shallow[0]   # [1, 2, 9]: внутренние списки общие
deep[0]      # [1, 2]

Поверхностная копия создаёт новый внешний контейнер, но кладёт в него те же внутренние объекты. Глубокая рекурсивно копирует всё, учитывает циклические ссылки и может быть медленной.

Частые ошибки

  • [[0] * 3] * 3 создаёт три ссылки на один и тот же внутренний список. Правильно: [[0] * 3 for _ in range(3)].
  • if x == None вместо if x is None.
  • Проверка if not items для значения, где 0 или '' допустимы: используйте is None.
  • Сравнение float через ==: берите math.isclose.
  • Мнение, что tuple всегда хешируем.

Что спросят на собесе

  • Чем изменяемые типы отличаются от неизменяемых, приведите примеры.
  • Разница между is и ==, почему a is b для чисел иногда True.
  • Что будет при b = a для списка и как сделать копию, чем поверхностная отличается от глубокой.
  • Почему 0.1 + 0.2 != 0.3 и что с этим делать.
  • Что считается ложным и что возвращают and и or.

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.