fa
Feedback
Python: задачки и вопросы

Python: задачки и вопросы

رفتن به کانال در Telegram

Вопросы и задачки для подготовки к собеседованиям и прокачки навыков Разместить рекламу: @tproger_sales_bot Правила общения: https://tprg.ru/rules Другие каналы: @tproger_channels Другие наши проекты: https://tprg.ru/media

نمایش بیشتر
6 964
مشترکین
اطلاعاتی وجود ندارد24 ساعت
-167 روز
-6430 روز
آرشیو پست ها
Развёрнутое пояснение: 1. Создаётся список orders = [120, 80, 300, 50]. 2. Создаётся генератор big = (o for o in orders if o > 100). Сам он ещё ничего не читает, а только запоминает источник и условие. 3. orders.append(400) изменяет исходный список: теперь orders = [120, 80, 300, 50, 400]. 4. sum(big) начинает итерировать генератор. На этом шаге он проходит по текущему списку и отбирает элементы больше 100: 120, 300, 400. 5. Сумма отобранных элементов равна 820. Почему это важно: ленивые итераторы не делают снимок данных в момент создания, а читают источник по мере потребления. В продакшене это встречается в ETL-пайплайнах, потоковой обработке и работе с большими файлами, где данные могут меняться между созданием пайплайна и его запуском. Понимание этого помогает избежать неожиданных результатов при обработке заказов, логов или метрик. Системно разбирают в Инженер данных от Практикума PRO, в модуле по ETL-пайплайнам и потоковой обработке.

Развёрнутое пояснение: В сигнатуре функции f параметры a и b стоят до /, поэтому их можно передать только позиционно. Параметр d стоит после *, поэтому его можно передать только по имени. Параметр c находится между / и *, поэтому допускает оба варианта. Корректен вызов с позиционными a, b и именованными c, d.

Развёрнутое пояснение: 1. Определяется класс Key с методом __eq__, который сравнивает атрибут val. 2. В Python класс, определяющий __eq__, но не определяющий __hash__, автоматически получает __hash__ = None. 3. При выполнении строки cache = {Key(1): 'hit'} Python пытается вычислить хеш ключа, чтобы разместить его в словаре. 4. Поскольку Key.__hash__ равен None, объект считается нехешируемым, и создание словаря падает с TypeError: unhashable type: 'Key'. 5. Вызов cache.get(Key(1)) не выполняется, потому что программа прерывается раньше. Почему это важно: кастомные ключи для кэшей, конфигураций или агрегаций часто требуют своего сравнения. Если забыть реализовать __hash__, весь механизм ломается ещё на этапе создания dict или set. В dataclasses это решается через frozen=True для автоматического __hash__, а в собственных классах — явным определением __hash__.

Развёрнутое пояснение: 1. Первый вызов load_users('a') не находит ключ в кэше, выполняет return [], сохраняет созданный список в кэше и сразу добавляет в него 'u1' через .append. Теперь в кэше лежит список ['u1']. 2. Второй вызов load_users('a') попадает в кэш и возвращает тот же самый объект-список, а не новый. .append('u2') изменяет этот же объект, и он становится ['u1', 'u2']. 3. Вызов print(load_users('a')) снова получает тот же объект из кэша и печатает ['u1', 'u2']. Почему это важно: кэширование изменяемых объектов — распространённая ловушка при оптимизации репозиториев, загрузчиков конфигурации и API-клиентов. Если возвращаемый объект мутируется, кэш превращается в разделяемое изменяемое состояние, и разные вызовы начинают влиять друг на друга, что приводит к трудноуловимым багам в продакшене.

Развёрнутое пояснение: 1. Вызов save(10) входит в функцию и сразу попадает в блок try. 2. Внутри try выполняется value / 0, что порождает исключение ZeroDivisionError. 3. Управление переходит в блок except ZeroDivisionError, который готовится вернуть строку "fail". 4. Перед тем как функция завершится, Python обязательно выполняет блок finally, независимо от того, было ли исключение. 5. В finally встречается собственный return "cleanup"; return в finally заменяет любой ранее подготовленный результат или даже не обработанное исключение. 6. Поэтому save(10) возвращает "cleanup", и print выводит cleanup. Почему это важно: return в finally перекрывает не только обычные значения, но и исключения, что легко превращает функцию очистки ресурсов в источник молчаливых потерь ошибок. В реальном коде finally стоит использовать только для освобождения ресурсов, закрытия соединений и снятия флагов, а итоговый результат или статус ошибки возвращать из try/except.

Развёрнутое пояснение: 1. Создаётся кортеж route = ('start',). 2. Вызов tag(route) передаёт ссылку на этот кортеж в параметр path. 3. Внутри tag выполняется path += ('end',): так как tuple неизменяемый, Python не может расширить существующий объект, а создаёт новый кортеж ('start', 'end') и присваивает его локальной переменной path. 4. Переменная route в вызывающем коде продолжает ссылаться на исходный кортеж ('start',). 5. print(route) выводит ('start',). Почему это важно: с неизменяемыми типами (tuple, str, int) оператор += никогда не меняет исходный объект, он лишь создаёт новое значение и переприсваивает локальную ссылку. Поэтому функция, которая должна «дополнить» переданный неизменяемый объект, должна явно вернуть результат, иначе вызывающий код не увидит изменений. Это частая ошибка при работе с путями, идентификаторами и конфигурационными ключами.

Напишите бэкенд онлайн-кинотеатра из 10+ микросервисов за 6 месяцев: 🤯 админка и ETL из PostgreSQL в Elasticsearch (Django, Docker, nginx); 🤯 асинхронный API поиска по базе фильмов (AsyncIO, FastAPI, Redis); 🤯 сервис авторизации: OAuth 2.0, OpenID, JWT; 🤯 UGC под большой поток событий: Kafka, ClickHouse, ELK, CI на GitHub Actions; 🤯 нотификации через RabbitMQ. Это программа курса «Мидл Python-разработчик» от Практикума PRO. Каждый модуль — отдельный сервис с новым стеком. Плюс Kubernetes: развернёте кластер и повесите Prometheus с Grafana на свои сервисы. Сам Python тут почти не преподают: предполагается, что язык вы знаете. Учат тому, чего не даёт маленький или узкоспециализированный проект: архитектура, отказоустойчивость, OLTP/OLAP, масштабирование. Итоговый проект — это онлайн-кинотеатр, доработанный сервисом на выбор (например, голосовым помощником), с защитой перед архитектурным комитетом. Кому подойдёт: у вас есть больше года коммерческой разработки на Python, знакомы с SQL и Linux; вы готовы к нагрузке в 15–20 часов в неделю (выпускники называют это второй работой). 💻 Проверьте свой уровень на входном тестировании: https://tprg.ru/QGe1 Реклама. Рекламодатель: АНО ДПО "Образовательные технологии Яндекса" ИНН 7704282033, erid: 2W5zFH6Ppp1

Развёрнутое пояснение: 1. Вызов fetch() входит в блок try. 2. В try возникает исключение ValueError('fail'). 3. Перед завершением функции выполняется блок finally. 4. В finally встречается return 'ok', который подавляет исключение и возвращает это значение. 5. print(fetch()) печатает ok. Почему это важно: return, break или continue в finally могут скрыть ошибки, поэтому cleanup-код не должен возвращать значений или прерывать поток управления.

Развёрнутое пояснение: 1. В main сначала вызывается create_task(f(2)), поэтому задача для f(2) встаёт в очередь цикла событий первой. 2. Затем вызывается create_task(f(1)), и задача для f(1) становится второй в очереди. 3. await t1 отдаёт управление циклу событий, который начинает выполнять отложенные задачи в порядке их постановки. 4. Сначала запускается f(2): она печатает 2, затем await asyncio.sleep(0) приостанавливает её. 5. Пока f(2) ждёт, цикл переключается на f(1), печатает 1 и тоже засыпает на 0. 6. Когда sleep(0) завершается, задачи просыпаются в том же порядке: f(2) печатает 12, затем f(1) печатает 11. 7. await t1 и await t2 завершаются, так как обе задачи уже выполнены. В stdout оказывается 2, 1, 12, 11. Почему это важно: в асинхронных сервисах create_task лишь планирует задачу, а не запускает её мгновенно. Порядок выполнения определяется очередью событий, поэтому await в конце не меняет порядок, заданный при создании задач. Это помогает избежать неожиданного порядка вызовов при инициализации ресурсов, логировании или конкурентной обработке событий.

Развёрнутое пояснение: 1. Создаём множество с тремя объектами: 1 (int), 1.0 (float), True (bool). 2. Python сначала сравнивает хэши: hash(1), hash(1.0) и hash(True) равны. 3. При совпадении хэшей множество проверяет равенство через ==: 1 == 1.0 == True возвращает True. 4. Все три значения признаются дубликатами, и в множестве остаётся только первый вставленный элемент — 1. 5. print(len(data)) выводит 1. Почему это важно В ETL-пайплайнах такое поведение приводит к потере строк: дедупликация ключей int/float/bool или группировка по ним сворачивает разные сущности в одну. Поэтому в ключах явно приводят типы и не смешивают bool с числами. 💻 ETL-автоматизация — это только один из этапов разработки архитектуры данных. Если хотите системно разобраться в этой сфере, посмотрите программу Инженер данных от Практикума PRO. Вы сделаете 8 проектов, в рамках которых научитесь обрабатывать данные разными инструментами, проектировать пайплайны, создавать витрины и хранилища. Обратите внимание, что курс на полгода и он не для начинающих: вам понадобятся уверенные знания Python и SQL. Реклама. Рекламодатель: АНО ДПО "Образовательные технологии Яндекса" ИНН 7704282033, erid: 2W5zFJBoozq

Развёрнутое пояснение: 1. Определяется класс Field с методами __set__ и __get__, что делает его дескриптором данных. 2. Класс Record получает атрибут класса value, равный экземпляру Field. 3. Создаётся r = Record(). 4. Присваивание r.value = 3 вызывает дескрипторный метод __set__, который записывает в r._val значение 3 * 10, то есть 30. 5. Прямая запись r.__dict__["value"] = 9 помещает 9 в словарь экземпляра, но не вызывает дескриптор и не заменяет атрибут класса. 6. При чтении r.value правило поиска атрибутов в Python сначала проверяет дескриптор данных в классе; он находит value = Field() и вызывает его __get__, возвращающий r._val, равное 30. 7. Значение 9 из __dict__ остаётся недоступным через обычный доступ r.value, пока дескриптор с __set__ существует. Почему это важно: В ORM, валидаторах и property с setter часто скрывают хранение значения за дескрипторами. Ошибка возникает, когда разработчик пишет obj.__dict__["field"] =... или сериализатор заполняет словарь напрямую, ожидая, что это эквивалентно obj.field =.... В действительности дескриптор данных переопределяет чтение, поэтому тесты могут показать одно значение, а обычный доступ — другое. Понимание приоритета дескрипторов помогает избежать рассинхронизации между внутренним хранилищем и публичным интерфейсом.

Развёрнутое пояснение: 1. Создаётся u с id=1; его __hash__ возвращает 1. 2. Словарь d помещает пару u: 'a' в корзину для хэша 1. 3. u.id меняется на 2; сам объект u всё ещё лежит в корзине 1, потому что словарь не пересчитывает хэш. 4. d.get(User(2)) вычисляет хэш нового объекта — 2 — и ищет в корзине 2. 5. В корзине 2 ничего нет, поэтому возвращается None. Почему это важно: ключи dict и элементы set должны быть неизменяемыми относительно величины, участвующей в __hash__. Если объект-ключ мутирует, он остаётся в старой корзине, и поиск по новому хэшу его не найдёт. Это приводит к утечкам в кэшах, потере данных в индексах и нестабильным тестам при использовании изменяемых сущностей в качестве ключей.