Python: задачки и вопросы
Развёрнутое пояснение:
1. Определяются две функции-генератора: inner и outer.
2. При вызове list(outer()) создаётся генератор outer.
3. Первый yield 1 в outer приостанавливает выполнение и возвращает значение 1.
4. Затем выполняется выражение v = yield from inner().
5. yield from делегирует управление генератору inner, поэтому следующим возвращается его yield 2.
6. Когда inner завершается оператором return 'done', это значение становится результатом выражения yield from.
7. Переменная v получает строку 'done'.
8. Последний yield v возвращает 'done'.
9. Генератор outer завершается, и list собирает все полученные значения: 1, 2 и 'done'.
10. print выводит [1, 2, 'done'].
Почему это важно: механика yield from используется при построении ленивых пайплайнов обработки данных, рекурсивных обходов деревьев и корутин. Понимание того, что return подгенератора становится возвращаемым значением yield from, помогает правильно комбинировать генераторы и передавать результаты между этапами пайплайна без промежуточных коллекций.
2. При вызове list(outer()) создаётся генератор outer.
3. Первый yield 1 в outer приостанавливает выполнение и возвращает значение 1.
4. Затем выполняется выражение v = yield from inner().
5. yield from делегирует управление генератору inner, поэтому следующим возвращается его yield 2.
6. Когда inner завершается оператором return 'done', это значение становится результатом выражения yield from.
7. Переменная v получает строку 'done'.
8. Последний yield v возвращает 'done'.
9. Генератор outer завершается, и list собирает все полученные значения: 1, 2 и 'done'.
10. print выводит [1, 2, 'done'].
Почему это важно: механика yield from используется при построении ленивых пайплайнов обработки данных, рекурсивных обходов деревьев и корутин. Понимание того, что return подгенератора становится возвращаемым значением yield from, помогает правильно комбинировать генераторы и передавать результаты между этапами пайплайна без промежуточных коллекций.
✍2
Python: задачки и вопросы
Развёрнутое пояснение:
1. Вызов save(5) создаёт пустой список result.
2. В try-блоке вычисляется value // 2, то есть 2, и это значение добавляется в result.
3. Достигается инструкция return result.
4. Перед фактическим возвратом управление обязательно переходит в finally-блок, так как finally выполняется при любом выходе из try/except.
5. В finally вычисляется value * 2, то есть 10, и это значение тоже добавляется в тот же список result.
6. Теперь result содержит [2, 10].
7. Функция возвращает ссылку на этот список.
8. print выводит [2, 10].
Почему это важно: в реальном коде finally используется для cleanup, метрик или освобождения ресурсов. Если при этом мутировать объект, который планируется вернуть из функции, финальное значение изменится ещё до передачи вызывающей стороне. Это стреляет в сценариях, когда return и cleanup работают с одной структурой данных, например при сборке ответа, логировании или обновлении кэша.
2. В try-блоке вычисляется value // 2, то есть 2, и это значение добавляется в result.
3. Достигается инструкция return result.
4. Перед фактическим возвратом управление обязательно переходит в finally-блок, так как finally выполняется при любом выходе из try/except.
5. В finally вычисляется value * 2, то есть 10, и это значение тоже добавляется в тот же список result.
6. Теперь result содержит [2, 10].
7. Функция возвращает ссылку на этот список.
8. print выводит [2, 10].
Почему это важно: в реальном коде finally используется для cleanup, метрик или освобождения ресурсов. Если при этом мутировать объект, который планируется вернуть из функции, финальное значение изменится ещё до передачи вызывающей стороне. Это стреляет в сценариях, когда return и cleanup работают с одной структурой данных, например при сборке ответа, логировании или обновлении кэша.
✍2
Python: задачки и вопросы
Развёрнутое пояснение:
1. Цикл for n in 'ba' регистрирует колбэки в порядке 'b', затем 'a'.
2. Вызов stack.close() запускает колбэки в обратном порядке: сначала 'a', потом 'b'.
3. Колбэк 'a' печатает a и выбрасывает RuntimeError('a'), но ExitStack ловит это исключение и продолжает выполнение оставшихся колбэков.
4. Колбэк 'b' печатает b и выбрасывает RuntimeError('b'); ExitStack запоминает уже это последнее исключение.
5. После завершения всех колбэков ExitStack поднимает сохранённое исключение, которое ловит внешний except и печатает caught b. Итоговый вывод: a, b, caught b.
Почему это важно: в реальных сервисах cleanup-колбэки закрывают соединения с базой, временные файлы или снимают блокировки. Порядок закрытия обратен порядку регистрации, при этом один упавший колбэк не должен прервать остальные освобождения ресурсов, иначе останутся висячие соединения или утечки.
2. Вызов stack.close() запускает колбэки в обратном порядке: сначала 'a', потом 'b'.
3. Колбэк 'a' печатает a и выбрасывает RuntimeError('a'), но ExitStack ловит это исключение и продолжает выполнение оставшихся колбэков.
4. Колбэк 'b' печатает b и выбрасывает RuntimeError('b'); ExitStack запоминает уже это последнее исключение.
5. После завершения всех колбэков ExitStack поднимает сохранённое исключение, которое ловит внешний except и печатает caught b. Итоговый вывод: a, b, caught b.
Почему это важно: в реальных сервисах cleanup-колбэки закрывают соединения с базой, временные файлы или снимают блокировки. Порядок закрытия обратен порядку регистрации, при этом один упавший колбэк не должен прервать остальные освобождения ресурсов, иначе останутся висячие соединения или утечки.
✍1
Python: задачки и вопросы
Развёрнутое пояснение:
1. Создаётся словарь base с ключом retry, значением которого является другой словарь {limit: 3}.
2. copy.copy(base) создаёт новый внешний словарь cfg, но значение по ключу retry — это тот же вложенный словарь, а не его отдельная копия.
3. Через cfg['retry']['limit'] = 5 изменяется значение во вложенном словаре, на который ссылаются и base, и cfg.
4. Поэтому base['retry']['limit'] теперь равно 5, и print выводит 5.
Почему это важно: в реальных проектах часто клонируют конфиг, кэш или настройки. Если использовать поверхностное копирование, вложенные изменяемые структуры остаются общими, и правки в одной копии неожиданно затрагивают другую. Для полной изоляции вложенных объектов нужен copy.deepcopy.
2. copy.copy(base) создаёт новый внешний словарь cfg, но значение по ключу retry — это тот же вложенный словарь, а не его отдельная копия.
3. Через cfg['retry']['limit'] = 5 изменяется значение во вложенном словаре, на который ссылаются и base, и cfg.
4. Поэтому base['retry']['limit'] теперь равно 5, и print выводит 5.
Почему это важно: в реальных проектах часто клонируют конфиг, кэш или настройки. Если использовать поверхностное копирование, вложенные изменяемые структуры остаются общими, и правки в одной копии неожиданно затрагивают другую. Для полной изоляции вложенных объектов нужен copy.deepcopy.
✍2
Развёрнутое пояснение
a * b умножает векторы поэлементно: первый элемент с первым, второй со вторым, третий с третьим. Результат — массив [4 10 18]. a @ b делает те же умножения и складывает их в одно число: 4 + 10 + 18 = 32. Это скалярное произведение, на нём построены линейная регрессия и косинусное сходство текстов.
Если перепутать операторы, код не упадёт: обе операции корректны для векторов одинаковой длины. Ошибка проявится только неправильными числами в результате, и заметить её можно только по смыслу этих чисел.
Почему это важно
Задача проверяет, различаете ли вы две операции по смыслу: где нужно перемножить признаки поэлементно, а где — свернуть векторы в одно число.
💻 Если знаете Python, а с математикой за NumPy знакомы хуже — посмотрите курс «Математика для анализа данных» Практикума PRO. Скалярное произведение, нормы и работа с векторами в NumPy — это модуль линейной алгебры, дальше матанализ до градиентного спуска, теорвер и статистика до A/B-тестов. 1000+ задач, практика на Python в тренажёре, в конце — симулятор математической секции собеседования.
Реклама. Рекламодатель: АНО ДПО "Образовательные технологии Яндекса" ИНН 7704282033, erid: 2W5zFFzVHzh
Если перепутать операторы, код не упадёт: обе операции корректны для векторов одинаковой длины. Ошибка проявится только неправильными числами в результате, и заметить её можно только по смыслу этих чисел.
Почему это важно
Задача проверяет, различаете ли вы две операции по смыслу: где нужно перемножить признаки поэлементно, а где — свернуть векторы в одно число.
Реклама. Рекламодатель: АНО ДПО "Образовательные технологии Яндекса" ИНН 7704282033, erid: 2W5zFFzVHzh
Please open Telegram to view this post
VIEW IN TELEGRAM
✍2
Первый модуль курса «Алгоритмы и структуры данных» в Практикуме PRO открыт бесплатно: 10 тем, около 7 часов.
Внутри — понятие алгоритма, метод скользящего среднего и метод двух указателей, пробные задачи с проверкой. Хватает, чтобы ещё до оплаты понять, нужен ли вам курс целиком.
Дальше в программе: оценка сложности, структуры данных, сортировки, хеш-таблицы, деревья, графы и динамическое программирование. 100+ задач, код-ревью от практикующих разработчиков и пробное алгоритмическое собеседование. Курс идёт 4 месяца при нагрузке около 10 часов в неделю.
💻 Открыть бесплатный модуль: https://tprg.ru/n6Cr
Реклама. Рекламодатель: АНО ДПО "Образовательные технологии Яндекса" ИНН 7704282033, erid: 2W5zFKAB2eV
Внутри — понятие алгоритма, метод скользящего среднего и метод двух указателей, пробные задачи с проверкой. Хватает, чтобы ещё до оплаты понять, нужен ли вам курс целиком.
Дальше в программе: оценка сложности, структуры данных, сортировки, хеш-таблицы, деревья, графы и динамическое программирование. 100+ задач, код-ревью от практикующих разработчиков и пробное алгоритмическое собеседование. Курс идёт 4 месяца при нагрузке около 10 часов в неделю.
Реклама. Рекламодатель: АНО ДПО "Образовательные технологии Яндекса" ИНН 7704282033, erid: 2W5zFKAB2eV
Please open Telegram to view this post
VIEW IN TELEGRAM
✍2
Python: задачки и вопросы
Развёрнутое пояснение:
1. Импортируется Counter из collections.
2. a получает значения x=2, y=1; b получает x=1, y=2.
3. Операция a - b вычитает счётчики по каждому ключу: x становится 2 - 1 = 1, y становится 1 - 2 = -1.
4. Counter автоматически отбрасывает ключи с неположительным значением, поэтому результат содержит только x=1.
5. Метод elements() возвращает итератор, который выдаёт каждый ключ столько раз, чему равен его положительный счётчик: здесь 'x' один раз.
6. list(...) превращает итератор в список ['x'], который и печатает print.
Почему это важно: при сравнении частот событий, тегов или ошибок за два периода часто используют вычитание Counter. Важно помнить, что отрицательные и нулевые значения исчезают из результата, а elements() не покажет отсутствующие ключи, иначе можно принять просевшие метрики за новые события.
2. a получает значения x=2, y=1; b получает x=1, y=2.
3. Операция a - b вычитает счётчики по каждому ключу: x становится 2 - 1 = 1, y становится 1 - 2 = -1.
4. Counter автоматически отбрасывает ключи с неположительным значением, поэтому результат содержит только x=1.
5. Метод elements() возвращает итератор, который выдаёт каждый ключ столько раз, чему равен его положительный счётчик: здесь 'x' один раз.
6. list(...) превращает итератор в список ['x'], который и печатает print.
Почему это важно: при сравнении частот событий, тегов или ошибок за два периода часто используют вычитание Counter. Важно помнить, что отрицательные и нулевые значения исчезают из результата, а elements() не покажет отсутствующие ключи, иначе можно принять просевшие метрики за новые события.
✍1
Python: задачки и вопросы
Развёрнутое пояснение:
Ключ сортировки — второе число пары, поэтому ('b', 1) и ('d', 1) для сортировки равны. Python гарантирует стабильность: равные по ключу элементы сохраняют исходный порядок. 'b' стояла раньше 'd', 'a' раньше 'c' — ответ A.
Гарантия прописана в документации. Ошибаются здесь обычно те, кто привык к C++, где std::sort стабильность не обещает и для неё существует отдельный std::stable_sort.
Практическое следствие: сортировка по нескольким критериям делается последовательными проходами от младшего ключа к старшему, без составных кортежей.
Почему это важно
Задача выглядит как вопрос про синтаксис, но отвечает на неё знание алгоритма: какие гарантии даёт сортировка, почему, чем это отличается в других языках.
💻 Если хочется разобраться, как устроены сортировки, — посмотрите курс « Алгоритмы и структуры данных » Практикума PRO, там под них отведён отдельный модуль. Решения проходят автопроверку в Яндекс Контесте с ограничениями по времени и памяти, финальные задачи смотрит ревьюер и требует обосновать сложность, в конце — пробное алгоритмическое собеседование с наставником.
Реклама. Рекламодатель: АНО ДПО "Образовательные технологии Яндекса" ИНН 7704282033, erid: 2W5zFK4PUL9
Please open Telegram to view this post
VIEW IN TELEGRAM
✍3
Python: задачки и вопросы
Развёрнутое пояснение:
1. Цикл for event_type in ('click', 'scroll') начинает первую итерацию: event_type = 'click'.
2. На этой итерации определяется функция handler(e=event_type). Значение по умолчанию для e вычисляется сейчас и сохраняется как 'click' внутри объекта функции.
3. Переменная event_type в теле handler не является локальной: она свободная и будет искаться в объемлющей области видимости во время вызова.
4. Вторая итерация: event_type = 'scroll'. Определяется новая функция handler, её значение по умолчанию для e — 'scroll'.
5. После цикла event_type остаётся равной 'scroll', так как это последнее значение итерируемого.
6. Вызов handlers[0]() использует значение по умолчанию e='click' и берёт event_type='scroll' из замыкания, возвращая ('click', 'scroll').
7. Вызов handlers[1]() использует e='scroll' и event_type='scroll', возвращая ('scroll', 'scroll').
Почему это важно: разница между ранним и поздним связыванием встречается при создании колбэков, обработчиков событий и фабрик функций в цикле. Ошибка приводит к тому, что все созданные обработчики ссылаются на последнее значение переменной цикла, а использование параметра по умолчанию — распространённый способ зафиксировать нужное значение для каждой функции.
2. На этой итерации определяется функция handler(e=event_type). Значение по умолчанию для e вычисляется сейчас и сохраняется как 'click' внутри объекта функции.
3. Переменная event_type в теле handler не является локальной: она свободная и будет искаться в объемлющей области видимости во время вызова.
4. Вторая итерация: event_type = 'scroll'. Определяется новая функция handler, её значение по умолчанию для e — 'scroll'.
5. После цикла event_type остаётся равной 'scroll', так как это последнее значение итерируемого.
6. Вызов handlers[0]() использует значение по умолчанию e='click' и берёт event_type='scroll' из замыкания, возвращая ('click', 'scroll').
7. Вызов handlers[1]() использует e='scroll' и event_type='scroll', возвращая ('scroll', 'scroll').
Почему это важно: разница между ранним и поздним связыванием встречается при создании колбэков, обработчиков событий и фабрик функций в цикле. Ошибка приводит к тому, что все созданные обработчики ссылаются на последнее значение переменной цикла, а использование параметра по умолчанию — распространённый способ зафиксировать нужное значение для каждой функции.
✍1
Плохо помните математику и боитесь её, но она нужна для работы? Присмотритесь к курсу «Математика для анализа данных» от Практикума PRO.
В рамках курса вы решите 100+ практических задач и подготовитесь к математической части собеседований аналитиков и специалистов по data science. А для этого погрузитесь в четыре раздела математики:
1. Линейная алгебра: векторы, матрицы, скалярное произведение, нормы, определители, смена базиса.
2. Функции: линейные и полиномиальные, логарифмы, модули, композиции функций, графики.
3. Матанализ: пределы, производные, интегралы, функции нескольких переменных и градиентный спуск.
4. Теорвер и матстат: дискретные и непрерывные случайные величины, распределения, теорема Байеса, корреляция, центральная предельная теорему, А/B-тестирование, гипотезы, p-value.
Можно прийти даже с забытыми школьными знаниями. Формулы разбирают на понятных жизненных задачах с кодом на Python и визуализациями. Есть преподаватель математики, который отвечает на вопросы по сложным задачам, а встроенный ИИ-помощник объяснит одну и ту же формулу разными словами.
💻 Протестировать платформу и формат обучения, а также пройти первый урок бесплатно: https://tprg.ru/WcrJ
В рамках курса вы решите 100+ практических задач и подготовитесь к математической части собеседований аналитиков и специалистов по data science. А для этого погрузитесь в четыре раздела математики:
1. Линейная алгебра: векторы, матрицы, скалярное произведение, нормы, определители, смена базиса.
Зачем. Работа с данными держится на линейной алгебре. Вектор — это просто строка с характеристиками объекта (например, клиент с параметрами: возраст, доход, количество покупок). Матрица — это вся таблица с данными. Линейная алгебра учит превращать текст или картинки в наборы чисел и вычислять, насколько объекты похожи друг на друга (например, для рекомендательных систем или поисковиков).
2. Функции: линейные и полиномиальные, логарифмы, модули, композиции функций, графики.
Зачем. Показывает, как описывать реальные процессы формулами. Например, как цена квартиры зависит от её площади или как меняется спрос на товар. Логарифмы позволяют работать с огромными разбросами значений (когда у одного пользователя 2 подписчика, а у другого — 10 миллионов), приводя их к удобному масштабу.
3. Матанализ: пределы, производные, интегралы, функции нескольких переменных и градиентный спуск.
Зачем. Помогает находить наилучшие (оптимальные) решения и обучать алгоритмы. Производная показывает скорость и направление изменений. Алгоритм градиентного спуска использует производные, чтобы заставлять нейросети и модели машинного обучения совершать всё меньше ошибок. Это главный механизм «обучения» ИИ.
4. Теорвер и матстат: дискретные и непрерывные случайные величины, распределения, теорема Байеса, корреляция, центральная предельная теорему, А/B-тестирование, гипотезы, p-value.
Зачем. Учит принимать обоснованные решения в условиях неопределенности. Статистика нужна, чтобы измерять взаимосвязи через A/B-тесты (например, действительно ли новая кнопка на сайте увеличила продажи или это просто случайность) и оценить надежность выводов.
Можно прийти даже с забытыми школьными знаниями. Формулы разбирают на понятных жизненных задачах с кодом на Python и визуализациями. Есть преподаватель математики, который отвечает на вопросы по сложным задачам, а встроенный ИИ-помощник объяснит одну и ту же формулу разными словами.
Please open Telegram to view this post
VIEW IN TELEGRAM
✍1
Python: задачки и вопросы
Развёрнутое пояснение:
1. Вызов await t(1) запускает первую корутину: на печать идёт 1, затем await sleep(0) приостанавливает выполнение main и отдаёт управление event loop.
2. create_task(t(3)) только планирует фоновую задачу в очередь event loop, но не запускает её немедленно.
3. await t(2) печатает 2 и тоже приостанавливается на sleep(0).
4. Event loop видит три готовых продолжения: t(1), запланированная t(3) и main после t(2). Порядок FIFO, поэтому сначала возобновляется t(1), которая печатает 2 и завершается.
5. Следующей запускается t(3): печатается 3, затем await sleep(0) приостанавливает её.
6. Затем event loop возвращает управление в main после await t(2), где сразу выполняется await x и main ждёт завершения t(3).
7. Event loop возобновляет t(3), печатается 4, задача завершается.
8. В результате на печать идёт последовательность 122334.
Почему это важно: в асинхронном коде побочные эффекты и порядок записи строк не всегда совпадают с порядком выполнения. sleep(0) — не пустая операция, а точка уступки управления, после которой event loop выбирает следующую задачу из очереди. Это влияет на порядок логов, обновление кэша, коммитов состояния и других операций с побочными эффектами, поэтому важно понимать, что create_task планирует задачу, а await передаёт управление.
2. create_task(t(3)) только планирует фоновую задачу в очередь event loop, но не запускает её немедленно.
3. await t(2) печатает 2 и тоже приостанавливается на sleep(0).
4. Event loop видит три готовых продолжения: t(1), запланированная t(3) и main после t(2). Порядок FIFO, поэтому сначала возобновляется t(1), которая печатает 2 и завершается.
5. Следующей запускается t(3): печатается 3, затем await sleep(0) приостанавливает её.
6. Затем event loop возвращает управление в main после await t(2), где сразу выполняется await x и main ждёт завершения t(3).
7. Event loop возобновляет t(3), печатается 4, задача завершается.
8. В результате на печать идёт последовательность 122334.
Почему это важно: в асинхронном коде побочные эффекты и порядок записи строк не всегда совпадают с порядком выполнения. sleep(0) — не пустая операция, а точка уступки управления, после которой event loop выбирает следующую задачу из очереди. Это влияет на порядок логов, обновление кэша, коммитов состояния и других операций с побочными эффектами, поэтому важно понимать, что create_task планирует задачу, а await передаёт управление.
✍1