Python: задачки и вопросы
Развёрнутое пояснение:
1. Декоратор dataclass(frozen=True) генерирует для Client методы __init__, __repr__, __eq__ и __hash__.
2. __eq__ у frozen dataclass сравнивает все поля экземпляров. У a name='a', у b name='b', поэтому a!= b.
3. __hash__ тоже строится по всем полям, поэтому hash(a)!= hash(b).
4. Словарь cache размещает a и b в разные корзины. После cache[a] = 'old' и cache[b] = 'new' в словаре два элемента.
5. Обращение cache[a] возвращает значение, связанное с a, то есть 'old'. Итоговый вывод: old 2.
Почему это важно: dataclass часто используют как ключи dict или элементы set, и поведение зависит от того, какие поля входят в равенство. frozen=True решает проблему изменяемости, но не помогает, если логика идентичности подразумевает только часть полей. В реальном коде это приводит к дублям в кеше, утечке памяти и неверным попаданиям при поиске.
2. __eq__ у frozen dataclass сравнивает все поля экземпляров. У a name='a', у b name='b', поэтому a!= b.
3. __hash__ тоже строится по всем полям, поэтому hash(a)!= hash(b).
4. Словарь cache размещает a и b в разные корзины. После cache[a] = 'old' и cache[b] = 'new' в словаре два элемента.
5. Обращение cache[a] возвращает значение, связанное с a, то есть 'old'. Итоговый вывод: old 2.
Почему это важно: dataclass часто используют как ключи dict или элементы set, и поведение зависит от того, какие поля входят в равенство. frozen=True решает проблему изменяемости, но не помогает, если логика идентичности подразумевает только часть полей. В реальном коде это приводит к дублям в кеше, утечке памяти и неверным попаданиям при поиске.
✍1
Python: задачки и вопросы
Развёрнутое пояснение:
1. Создаётся внутренний список ['', '', ''] — три пустые строки.
2. Выражение [внутренний_список] * 3 создаёт новый список из трёх элементов, но каждый элемент — это ссылка на один и тот же внутренний список, а не его независимая копия.
3. Присваивание grid[0][0] = 'X' изменяет первый элемент этого общего внутреннего списка, поэтому все три строки grid отображают одно и то же изменение.
4. print(grid) выводит [['X', '', ''], ['X', '', ''], ['X', '', '']].
Почему это важно: такая инициализация часто встречается при создании матриц, игровых полей, таблиц или двумерных кэшей. Если строки оказываются общим изменяемым объектом, мутация одной ячейки неожиданно распространяется на весь столбец или всю таблицу, что приводит к трудноуловимым багам. Правильный способ — создавать каждую строку отдельно, например, через списочное включение [['' for _ in range(3)] for _ in range(3)].
2. Выражение [внутренний_список] * 3 создаёт новый список из трёх элементов, но каждый элемент — это ссылка на один и тот же внутренний список, а не его независимая копия.
3. Присваивание grid[0][0] = 'X' изменяет первый элемент этого общего внутреннего списка, поэтому все три строки grid отображают одно и то же изменение.
4. print(grid) выводит [['X', '', ''], ['X', '', ''], ['X', '', '']].
Почему это важно: такая инициализация часто встречается при создании матриц, игровых полей, таблиц или двумерных кэшей. Если строки оказываются общим изменяемым объектом, мутация одной ячейки неожиданно распространяется на весь столбец или всю таблицу, что приводит к трудноуловимым багам. Правильный способ — создавать каждую строку отдельно, например, через списочное включение [['' for _ in range(3)] for _ in range(3)].
✍1
Python: задачки и вопросы
Развёрнутое пояснение:
1. Создаётся список batch с двумя разными словарями.
2. Вызов enrich(batch) возвращает генератор, тело которого пока не выполняется.
3. next(stream) запускает цикл, обрабатывает первый словарь, добавляет в него ключ status и приостанавливается на yield. Первый словарь уже изменён.
4. list(stream) возобновляет генератор, дообрабатывает второй словарь, добавляет тот же ключ и завершает итерацию.
5. print(batch) выводит исходный список, в котором теперь оба словаря содержат status со значением ok.
Почему это важно: ленивые пайплайны часто используются для обработки больших потоков данных, и мутация исходных объектов внутри генератора — распространённая ошибка. Если потребитель прочитает только часть потока или если несколько задач делят один источник, состояние исходных записей окажется непредсказуемым. Безопаснее возвращать копии или использовать неизменяемые структуры данных.
2. Вызов enrich(batch) возвращает генератор, тело которого пока не выполняется.
3. next(stream) запускает цикл, обрабатывает первый словарь, добавляет в него ключ status и приостанавливается на yield. Первый словарь уже изменён.
4. list(stream) возобновляет генератор, дообрабатывает второй словарь, добавляет тот же ключ и завершает итерацию.
5. print(batch) выводит исходный список, в котором теперь оба словаря содержат status со значением ok.
Почему это важно: ленивые пайплайны часто используются для обработки больших потоков данных, и мутация исходных объектов внутри генератора — распространённая ошибка. Если потребитель прочитает только часть потока или если несколько задач делят один источник, состояние исходных записей окажется непредсказуемым. Безопаснее возвращать копии или использовать неизменяемые структуры данных.
✍1
А вы уже забрали свой подарок ко Дню программиста?
Мы в Tproger вместе с нашими друзьями собрали целую коробку подарков к вашему профессиональному празднику. Переходите по ссылке, трясите коробку и забирайте свой презент: https://tprg.ru/pLwZ
Мы в Tproger вместе с нашими друзьями собрали целую коробку подарков к вашему профессиональному празднику. Переходите по ссылке, трясите коробку и забирайте свой презент: https://tprg.ru/pLwZ
Python: задачки и вопросы
Развёрнутое пояснение:
1. Вызывается persist_order, и блок try печатает строку write.
2. Инструкция raise создаёт исключение TimeoutError, после чего управление обязательно переходит в блок finally.
3. Блок finally печатает release и выполняет return со значением queued.
4. Этот return завершает функцию обычным образом и подавляет ещё не обработанное исключение TimeoutError.
5. Внешний print получает строку queued и печатает её, поэтому итоговый вывод состоит из write, release и queued.
Почему это важно
Задача относится к управлению исключениями и освобождению ресурсов. return внутри finally может незаметно скрыть сбой базы данных, из-за чего вызывающий код сочтёт операцию успешной. В прикладном коде блок finally обычно оставляют для очистки, а результат возвращают вне него.
2. Инструкция raise создаёт исключение TimeoutError, после чего управление обязательно переходит в блок finally.
3. Блок finally печатает release и выполняет return со значением queued.
4. Этот return завершает функцию обычным образом и подавляет ещё не обработанное исключение TimeoutError.
5. Внешний print получает строку queued и печатает её, поэтому итоговый вывод состоит из write, release и queued.
Почему это важно
Задача относится к управлению исключениями и освобождению ресурсов. return внутри finally может незаметно скрыть сбой базы данных, из-за чего вызывающий код сочтёт операцию успешной. В прикладном коде блок finally обычно оставляют для очистки, а результат возвращают вне него.
✍2
Python: задачки и вопросы
Развёрнутое пояснение:
1. make_handlers создаёт пустой список handlers.
2. Цикл дважды присваивает переменной status значения 'new' и 'done'.
3. На каждой итерации создаётся lambda, но текущее значение status внутрь неё не копируется.
4. После цикла обе функции обращаются к одной переменной status, сохранившей последнее значение 'done'.
5. Последовательный вызов обработчиков поэтому возвращает список ['done', 'done'].
Почему это важно
Задача относится к позднему связыванию имён в замыканиях. Такая ситуация встречается при создании обработчиков событий и отложенных задач в цикле: без отдельной фиксации значения все обработчики могут работать с последним элементом.
2. Цикл дважды присваивает переменной status значения 'new' и 'done'.
3. На каждой итерации создаётся lambda, но текущее значение status внутрь неё не копируется.
4. После цикла обе функции обращаются к одной переменной status, сохранившей последнее значение 'done'.
5. Последовательный вызов обработчиков поэтому возвращает список ['done', 'done'].
Почему это важно
Задача относится к позднему связыванию имён в замыканиях. Такая ситуация встречается при создании обработчиков событий и отложенных задач в цикле: без отдельной фиксации значения все обработчики могут работать с последним элементом.
✍1