Подробное объяснение
1️⃣ a = Singleton(): _inst равен None → создаётся объект → _inst заполнен.
2️⃣ b = Sub(): Sub не переопределяет __new__, использует родительский.
3️⃣ Внутри __new__ проверяется cls._inst. Python ищет _inst в MRO: Sub.__dict__ → Singleton.__dict__.
4️⃣ Находит _inst в Singleton (уже не None) → возвращает тот же объект.
5️⃣ a is b → True. Тип объекта — Singleton (создан через super().__new__(cls) в первый раз).
6️⃣ Вывод: True Singleton Singleton.
Почему это важно?
Наследование ломает изоляцию Singleton. Каждый подкласс должен иметь свой _inst, иначе все клонируют объект родителя.
2️⃣ b = Sub(): Sub не переопределяет __new__, использует родительский.
3️⃣ Внутри __new__ проверяется cls._inst. Python ищет _inst в MRO: Sub.__dict__ → Singleton.__dict__.
4️⃣ Находит _inst в Singleton (уже не None) → возвращает тот же объект.
5️⃣ a is b → True. Тип объекта — Singleton (создан через super().__new__(cls) в первый раз).
6️⃣ Вывод: True Singleton Singleton.
Почему это важно?
Наследование ломает изоляцию Singleton. Каждый подкласс должен иметь свой _inst, иначе все клонируют объект родителя.
✍2
Подробное объяснение
1️⃣ hash(1) == hash(1.0) == hash(True) == 1.
2️⃣ 1 == 1.0 == True → все дубликаты в set → len → 1.
3️⃣ a is b → 1 is 1.0 → разные типы, разные объекты → False.
4️⃣ a is c → 1 is True → False.
5️⃣ Вывод: 1 False False.
Почему это важно
is для чисел — антипаттерн. Работает случайно для малых чисел, ломается в production. Всегда используйте ==.
1️⃣ hash(1) == hash(1.0) == hash(True) == 1.
2️⃣ 1 == 1.0 == True → все дубликаты в set → len → 1.
3️⃣ a is b → 1 is 1.0 → разные типы, разные объекты → False.
4️⃣ a is c → 1 is True → False.
5️⃣ Вывод: 1 False False.
Почему это важно
is для чисел — антипаттерн. Работает случайно для малых чисел, ломается в production. Всегда используйте ==.
✍3
Подробное объяснение
1️⃣@dataclass создаёт __init__(self, tags=[]) — пустой список вычисляется один раз при определении класса.
2️⃣ u1 = User() и u2 = User() получают ссылку на один и тот же список.
3️⃣ u1.tags.append("admin") мутирует этот список.
4️⃣ u2.tags видит ту же мутацию — выводит ['admin'].
5️⃣ Правильно: tags: list = field(default_factory=list).
Почему это важно
Тот же баг, что и в обычной функции, но скрыт за декоратором. default_factory создаёт новый объект для каждого экземпляра.
1️⃣
2️⃣ u1 = User() и u2 = User() получают ссылку на один и тот же список.
3️⃣ u1.tags.append("admin") мутирует этот список.
4️⃣ u2.tags видит ту же мутацию — выводит ['admin'].
5️⃣ Правильно: tags: list = field(default_factory=list).
Почему это важно
Тот же баг, что и в обычной функции, но скрыт за декоратором. default_factory создаёт новый объект для каждого экземпляра.
✍3
Подробное объяснение
1️⃣ case Animal() проверяет isinstance(d, Animal).
2️⃣ Dog — подкласс Animal, поэтому проверка возвращает True.
3️⃣ Выполняется print("A"), блок case Dog() пропускается.
4️⃣ Match не падает сквозь — только первый подходящий case.
5️⃣ print("done") выполняется в любом случае.
Почему это важно
Более общий паттерн сверху перехватывает частные. Чтобы Dog сработал — поменяйте порядок: Dog выше Animal.
2️⃣ Dog — подкласс Animal, поэтому проверка возвращает True.
3️⃣ Выполняется print("A"), блок case Dog() пропускается.
4️⃣ Match не падает сквозь — только первый подходящий case.
5️⃣ print("done") выполняется в любом случае.
Почему это важно
Более общий паттерн сверху перехватывает частные. Чтобы Dog сработал — поменяйте порядок: Dog выше Animal.
✍3
Подробное объяснение
1️⃣ next(g) → yield 1 → возвращает 1.
2️⃣ next(g) → yield 2 → возвращает 2, останавливается на x = yield 2.
3️⃣ g.send(10) → отправляет 10 в левую часть yield → x = 10.
4️⃣ Генератор продолжается до yield x → возвращает 10.
5️⃣ Итог: 1 2 10.
Почему это важно
send() — основа корутин в asyncio. Понимание двунаправленной коммуникации с генератором критично для асинхронного кода.
1️⃣ next(g) → yield 1 → возвращает 1.
2️⃣ next(g) → yield 2 → возвращает 2, останавливается на x = yield 2.
3️⃣ g.send(10) → отправляет 10 в левую часть yield → x = 10.
4️⃣ Генератор продолжается до yield x → возвращает 10.
5️⃣ Итог: 1 2 10.
Почему это важно
send() — основа корутин в asyncio. Понимание двунаправленной коммуникации с генератором критично для асинхронного кода.
✍1
Подробное объяснение
1️⃣ Python 3 компилирует super() без аргументов в super(__class__, <first_arg>).
2️⃣ Компилятор создаёт __classcell__ — ячейку замыкания с классом B.
3️⃣ Внутри inner() Python ищет __class__ в замыканиях — находит B.
4️⃣ self берётся из параметра f(self) через цепочку замыканий.
5️⃣ super().f() → A.f(self) → возвращает "A".
Почему это важно
В Python 2 super(B, self) было обязательно. В Python 3 zero-argument super() работает через магию компилятора — даже во вложенных функциях.
1️⃣ Python 3 компилирует super() без аргументов в super(__class__, <first_arg>).
2️⃣ Компилятор создаёт __classcell__ — ячейку замыкания с классом B.
3️⃣ Внутри inner() Python ищет __class__ в замыканиях — находит B.
4️⃣ self берётся из параметра f(self) через цепочку замыканий.
5️⃣ super().f() → A.f(self) → возвращает "A".
Почему это важно
В Python 2 super(B, self) было обязательно. В Python 3 zero-argument super() работает через магию компилятора — даже во вложенных функциях.
✍2
Выражение a + [3] порождает новый список, который затем присваивается переменной a, не затрагивая исходный объект, на который ссылается b. Если бы использовалось a += [3], вызвался бы метод __iadd__, изменяющий список на месте, и тогда b тоже бы изменился. Таким образом, оператор присваивания с + не модифицирует исходный список.
✍3