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 запускает конкурентную задачу при ближайшей передаче управления циклу событий. Ожидание одной задачи не мешает другим уже запланированным задачам начать работу, что важно учитывать при отправке данных, обновлении метрик и обработке очередей.
✍4
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