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
Python: задачки и вопросы
Развёрнутое пояснение:
1. Вызывается load_config, управление переходит в блок try.
2. Первый print выводит строку read.
3. Инструкция raise создаёт ValueError, но перед выходом из функции обязательно выполняется блок finally.
4. В finally сначала выводится cleanup, затем return возвращает словарь с режимом safe и одновременно отменяет распространение ValueError.
5. Вызов load_config успешно завершается, словарь присваивается переменной config.
6. Последний print получает значение по ключу mode и выводит safe, поэтому итоговый stdout состоит из строк read, cleanup и safe.
Почему это важно: задача проверяет управление потоком при сочетании исключений и finally. В загрузчиках конфигурации, транзакциях и очистке ресурсов return внутри finally может скрыть исходную ошибку, из-за чего система продолжит работу с резервным результатом без ожидаемого сообщения о сбое.
2. Первый print выводит строку read.
3. Инструкция raise создаёт ValueError, но перед выходом из функции обязательно выполняется блок finally.
4. В finally сначала выводится cleanup, затем return возвращает словарь с режимом safe и одновременно отменяет распространение ValueError.
5. Вызов load_config успешно завершается, словарь присваивается переменной config.
6. Последний print получает значение по ключу mode и выводит safe, поэтому итоговый stdout состоит из строк read, cleanup и safe.
Почему это важно: задача проверяет управление потоком при сочетании исключений и finally. В загрузчиках конфигурации, транзакциях и очистке ресурсов return внутри finally может скрыть исходную ошибку, из-за чего система продолжит работу с резервным результатом без ожидаемого сообщения о сбое.
✍2
Python: задачки и вопросы
Развёрнутое пояснение:
1. Вызов build_batch создаёт генератор, но его тело пока не исполняется.
2. list начинает потреблять этот генератор, и build_batch передаёт управление load_orders через yield from.
3. load_orders выдаёт строку order-1, поэтому она попадает в список, собираемый функцией list.
4. Затем load_orders выполняет return 1: для генератора это завершение со значением 1, а не выдача ещё одного элемента.
5. yield from извлекает это значение завершения и присваивает его переменной loaded.
6. Внутренний print выводит loaded 1, после чего build_batch завершается.
7. list возвращает список из единственного выданного элемента, и внешний print выводит ['order-1'] второй строкой.
Почему это важно
Задача относится к делегированию генераторов. Значение return вложенного генератора можно использовать как итог обработки, например как число загруженных записей, но оно не становится элементом ленивого потока.
2. list начинает потреблять этот генератор, и build_batch передаёт управление load_orders через yield from.
3. load_orders выдаёт строку order-1, поэтому она попадает в список, собираемый функцией list.
4. Затем load_orders выполняет return 1: для генератора это завершение со значением 1, а не выдача ещё одного элемента.
5. yield from извлекает это значение завершения и присваивает его переменной loaded.
6. Внутренний print выводит loaded 1, после чего build_batch завершается.
7. list возвращает список из единственного выданного элемента, и внешний print выводит ['order-1'] второй строкой.
Почему это важно
Задача относится к делегированию генераторов. Значение return вложенного генератора можно использовать как итог обработки, например как число загруженных записей, но оно не становится элементом ленивого потока.
✍2
Python: задачки и вопросы
Развёрнутое пояснение:
1. В defaults создаётся словарь, где ключ retry ссылается на вложенный словарь со значением limit, равным 3.
2. Метод defaults.copy создаёт новый внешний словарь worker.
3. Вложенный словарь не копируется: значения defaults["retry"] и worker["retry"] ссылаются на один объект.
4. Присваивание через worker меняет limit в этом общем вложенном словаре на 5.
5. Поэтому обращение через defaults видит то же изменение, и print выводит 5.
Почему это важно
Это различие между поверхностным и глубоким копированием. Оно встречается при создании рабочих конфигураций из шаблона: изменение вложенного раздела поверхностной копии может незаметно изменить исходные настройки и повлиять на другие обработчики.
2. Метод defaults.copy создаёт новый внешний словарь worker.
3. Вложенный словарь не копируется: значения defaults["retry"] и worker["retry"] ссылаются на один объект.
4. Присваивание через worker меняет limit в этом общем вложенном словаре на 5.
5. Поэтому обращение через defaults видит то же изменение, и print выводит 5.
Почему это важно
Это различие между поверхностным и глубоким копированием. Оно встречается при создании рабочих конфигураций из шаблона: изменение вложенного раздела поверхностной копии может незаметно изменить исходные настройки и повлиять на другие обработчики.
✍1
Python: задачки и вопросы
Развёрнутое пояснение:
1. При входе в блок with вызывается метод __enter__, который печатает begin.
2. Тело блока печатает write, после чего возбуждает исключение ValueError.
3. Перед выходом из блока Python вызывает __exit__ и передаёт ему тип, значение и трассировку исключения.
4. __exit__ печатает имя типа исключения — ValueError — и возвращает True.
5. Истинное возвращаемое значение сообщает Python, что исключение обработано, поэтому строка после блока выполняется и печатает continued. Итоговый вывод: begin, write, ValueError и continued на отдельных строках.
Почему это важно
Задача проверяет протокол менеджеров контекста. На практике __exit__ часто освобождает соединения, завершает транзакции и управляет распространением ошибок; случайное возвращение истинного значения может скрыть сбой и позволить программе продолжить работу с некорректным состоянием.
2. Тело блока печатает write, после чего возбуждает исключение ValueError.
3. Перед выходом из блока Python вызывает __exit__ и передаёт ему тип, значение и трассировку исключения.
4. __exit__ печатает имя типа исключения — ValueError — и возвращает True.
5. Истинное возвращаемое значение сообщает Python, что исключение обработано, поэтому строка после блока выполняется и печатает continued. Итоговый вывод: begin, write, ValueError и continued на отдельных строках.
Почему это важно
Задача проверяет протокол менеджеров контекста. На практике __exit__ часто освобождает соединения, завершает транзакции и управляет распространением ошибок; случайное возвращение истинного значения может скрыть сбой и позволить программе продолжить работу с некорректным состоянием.
✍1
Python: задачки и вопросы
Развёрнутое пояснение:
1. asyncio.run запускает цикл событий и начинает выполнять main.
2. Первый вызов create_task планирует задачу db, но send ещё не начинает исполняться, поскольку main пока не передал управление циклу событий.
3. Второй вызов create_task аналогично планирует задачу log.
4. На await db функция main приостанавливается, и цикл событий запускает запланированные задачи в порядке их постановки.
5. Задача db печатает send db и приостанавливается на asyncio.sleep(0).
6. Затем задача log печатает send log и тоже приостанавливается.
7. После завершения db ожидание в main заканчивается, и main печатает saved. Поэтому строки выводятся в порядке send db, send log, saved.
Почему это важно: create_task запускает конкурентную задачу при ближайшей передаче управления циклу событий. Ожидание одной задачи не мешает другим уже запланированным задачам начать работу, что важно учитывать при отправке данных, обновлении метрик и обработке очередей.
2. Первый вызов create_task планирует задачу db, но send ещё не начинает исполняться, поскольку main пока не передал управление циклу событий.
3. Второй вызов create_task аналогично планирует задачу log.
4. На await db функция main приостанавливается, и цикл событий запускает запланированные задачи в порядке их постановки.
5. Задача db печатает send db и приостанавливается на asyncio.sleep(0).
6. Затем задача log печатает send log и тоже приостанавливается.
7. После завершения db ожидание в main заканчивается, и main печатает saved. Поэтому строки выводятся в порядке send db, send log, saved.
Почему это важно: create_task запускает конкурентную задачу при ближайшей передаче управления циклу событий. Ожидание одной задачи не мешает другим уже запланированным задачам начать работу, что важно учитывать при отправке данных, обновлении метрик и обработке очередей.
✍3
Python: задачки и вопросы
Развёрнутое пояснение:
1. Для класса Client строится порядок разрешения методов: Client, Audit, Retry, Base, object.
2. Вызов send сначала находит реализацию в Audit, которая создаёт список с элементом audit.
3. super() внутри Audit продолжает поиск после Audit, поэтому вызывает Retry.send, а не Base.send напрямую.
4. Retry добавляет retry и через свой super() передаёт управление методу Base.send.
5. Base.send возвращает список с base; результаты последовательно объединяются, поэтому вывод равен ['audit', 'retry', 'base'].
Почему это важно: задача проверяет кооперативное наследование и порядок разрешения методов. Такой подход применяется в примесях для журналирования, повторных попыток и проверки доступа; каждый класс должен передавать управление через super(), чтобы вся цепочка выполнилась.
2. Вызов send сначала находит реализацию в Audit, которая создаёт список с элементом audit.
3. super() внутри Audit продолжает поиск после Audit, поэтому вызывает Retry.send, а не Base.send напрямую.
4. Retry добавляет retry и через свой super() передаёт управление методу Base.send.
5. Base.send возвращает список с base; результаты последовательно объединяются, поэтому вывод равен ['audit', 'retry', 'base'].
Почему это важно: задача проверяет кооперативное наследование и порядок разрешения методов. Такой подход применяется в примесях для журналирования, повторных попыток и проверки доступа; каждый класс должен передавать управление через super(), чтобы вся цепочка выполнилась.
✍2