1️⃣Список 𝚗𝚞𝚖𝚜 содержит 𝟹 элемента с индексами 𝟶, 𝟷, 𝟸.
2️⃣Индексация 𝚗𝚞𝚖𝚜[𝟷𝟶𝟶] требует конкретный элемент — если его нет, это ошибка.
3️⃣Срез 𝚗𝚞𝚖𝚜[𝟷𝟶𝟶:] означает «от индекса 𝟷𝟶𝟶 до конца». Начало среза (𝟷𝟶𝟶) превышает длину списка, поэтому диапазон пуст.
4️⃣Python не считает это ошибкой: срез «пустого диапазона» — это просто пустой список [].
Аналогично, 𝚗𝚞𝚖𝚜[𝟷:𝟷𝟶𝟶] вернёт [𝟸, 𝟹], а не ошибку.
Почему это важно Это удобно для безопасной работы с данными неизвестной длины, но может маскировать логические ошибки: код молча вернёт пустой список вместо падения, и баг останется незамеченным.
1️⃣Список 𝚊 содержит два вложенных списка: [𝟷, 𝟸] и [𝟹, 𝟺].
2️⃣Вызов 𝚊.𝚌𝚘𝚙𝚢() создаёт новый внешний список 𝚋, но элементы 𝚋[𝟶] и 𝚋[𝟷] — это те же самые объекты, что и 𝚊[𝟶] и 𝚊[𝟷], не их копии.
3️⃣Когда выполняется 𝚊[𝟶].𝚊𝚙𝚙𝚎𝚗𝚍(𝟻), изменяется вложенный список, на который указывают и 𝚊[𝟶], и 𝚋[𝟶].
4️⃣Поэтому при печати 𝚋 видим [[𝟷, 𝟸, 𝟻], [𝟹, 𝟺]].
Почему это важно Для полного копирования вложенных структур нужен 𝚌𝚘𝚙𝚢.𝚍𝚎𝚎𝚙𝚌𝚘𝚙𝚢(). Поверхностная копия через 𝚕𝚒𝚜𝚝.𝚌𝚘𝚙𝚢(), 𝚕𝚒𝚜𝚝(...) или срез [:] — частый источник багов с «призрачными» изменениями в якобы независимых данных.
🔘При создании 𝙲𝚑𝚒𝚕𝚍() вызывается 𝙲𝚑𝚒𝚕𝚍.__𝚒𝚗𝚒𝚝__(), который устанавливает только 𝚜𝚎𝚕𝚏.𝚊𝚐𝚎 = 𝟷𝟶.
🔘Родительский 𝙿𝚊𝚛𝚎𝚗𝚝.__𝚒𝚗𝚒𝚝__() не вызывается автоматически — Python не делает этого за вас.
🔘Атрибут 𝚗𝚊𝚖𝚎 не существует у объекта 𝚌.
🔘𝚌.𝚊𝚐𝚎 возвращает 𝟷𝟶, но 𝚌.𝚗𝚊𝚖𝚎 выбрасывает AttributeError.
Почему это важно В отличие от некоторых языков, Python не вызывает конструктор родителя автоматически. Если дочерний класс переопределяет __𝚒𝚗𝚒𝚝__, нужно явно вызвать 𝚜𝚞𝚙𝚎𝚛().__𝚒𝚗𝚒𝚝__(...), иначе инициализация родителя не произойдёт.
🔘𝚃𝚛𝚞𝚎 — это объект типа 𝚋𝚘𝚘𝚕, а 𝚋𝚘𝚘𝚕 — подкласс 𝚒𝚗𝚝.
🔘𝚒𝚜𝚒𝚗𝚜𝚝𝚊𝚗𝚌𝚎(𝚃𝚛𝚞𝚎, 𝚒𝚗𝚝) проверяет: «является ли 𝚃𝚛𝚞𝚎 экземпляром 𝚒𝚗𝚝 или его подкласса?» — да, потому что 𝚋𝚘𝚘𝚕 наследуется от 𝚒𝚗𝚝. Результат: True.
🔘𝚝𝚢𝚙𝚎(𝚃𝚛𝚞𝚎) == 𝚒𝚗𝚝 проверяет: «является ли точный тип 𝚃𝚛𝚞𝚎 именно 𝚒𝚗𝚝?» — нет, точный тип это 𝚋𝚘𝚘𝚕. Результат: False.
Почему это важно Для проверки типов почти всегда лучше использовать 𝚒𝚜𝚒𝚗𝚜𝚝𝚊𝚗𝚌𝚎, потому что она учитывает наследование. Проверка 𝚝𝚢𝚙𝚎(...) == нужна редко — только когда важен именно точный тип, без подклассов.
1️⃣Исторически в Python не было типа 𝚋𝚘𝚘𝚕 — использовались 𝟶 и 𝟷. Когда 𝚋𝚘𝚘𝚕 добавили, его сделали подклассом 𝚒𝚗𝚝 для обратной совместимости.
2️⃣𝚃𝚛𝚞𝚎 ведёт себя как 𝟷 в арифметических операциях: 𝚃𝚛𝚞𝚎 + 𝚃𝚛𝚞𝚎 = 𝟸, потом 𝟸 + 𝚃𝚛𝚞𝚎 = 𝟹.
3️⃣Это позволяет считать количество истинных значений: 𝚜𝚞𝚖([𝚃𝚛𝚞𝚎, 𝙵𝚊𝚕𝚜𝚎, 𝚃𝚛𝚞𝚎]) вернёт 𝟸.
4️⃣Результат сложения — тип 𝚒𝚗𝚝, а не 𝚋𝚘𝚘𝚕.
Почему это важно С одной стороны, это удобно для подсчёта: 𝚜𝚞𝚖(𝚡 > 𝟶 𝚏𝚘𝚛 𝚡 𝚒𝚗 𝚗𝚞𝚖𝚜). С другой — может удивить при отладке или сериализации, когда вместо 𝚃𝚛𝚞𝚎 вдруг появляется число.
1️⃣Оператор 𝚒𝚗 для строк проверяет, является ли левый операнд подстрокой правого.
2️⃣Пустая строка "" — это строка длины 𝟶. По определению, она является подстрокой любой строки: можно представить, что пустая строка «находится» перед 𝚑, между 𝚑 и 𝚎, между 𝚎 и 𝚕, и так далее.