Python: задачки и вопросы
6.99K subscribers
1.35K photos
1 video
1 file
134 links
Вопросы и задачки для подготовки к собеседованиям и прокачки навыков

Разместить рекламу: @tproger_sales_bot

Правила общения: https://tprg.ru/rules

Другие каналы: @tproger_channels

Другие наши проекты: https://tprg.ru/media
Download Telegram
Please open Telegram to view this post
VIEW IN TELEGRAM
4
Python: задачки и вопросы
Развёрнутое пояснение:

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

Почему это важно: return, break или continue в finally могут скрыть ошибки, поэтому cleanup-код не должен возвращать значений или прерывать поток управления.
5
Напишите бэкенд онлайн-кинотеатра из 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
Please open Telegram to view this post
VIEW IN TELEGRAM
2
Please open Telegram to view this post
VIEW IN TELEGRAM
2
Python: задачки и вопросы
Развёрнутое пояснение:

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) оператор += никогда не меняет исходный объект, он лишь создаёт новое значение и переприсваивает локальную ссылку. Поэтому функция, которая должна «дополнить» переданный неизменяемый объект, должна явно вернуть результат, иначе вызывающий код не увидит изменений. Это частая ошибка при работе с путями, идентификаторами и конфигурационными ключами.
1
Please open Telegram to view this post
VIEW IN TELEGRAM
2
Python: задачки и вопросы
Развёрнутое пояснение:

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.
2
Please open Telegram to view this post
VIEW IN TELEGRAM
2
Python: задачки и вопросы
Развёрнутое пояснение:

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-пайплайнах, потоковой обработке и работе с большими файлами, где данные могут меняться между созданием пайплайна и его запуском. Понимание этого помогает избежать неожиданных результатов при обработке заказов, логов или метрик.
2
Please open Telegram to view this post
VIEW IN TELEGRAM
2
Python: задачки и вопросы
Развёрнутое пояснение:

1. В классе OrderBatch определён атрибут items, который ссылается на пустой список. Это атрибут класса, а не экземпляра.

2. Создание a и b не создаёт новых списков, оба экземпляра через поиск атрибутов находят один общий список класса.

3. a.items.append(1) изменяет этот общий список, добавляя в него 1.

4. print(b.items) обращается к тому же атрибуту класса и выводит [1].

Почему это важно: class attributes часто используются как значения по умолчанию, а пустые изменяемые коллекции в них — классическая ошибка, приводящая к общему состоянию между экземплярами. Это проявляется в тестах, бэкенд-сервисах и кэшах, где экземпляры начинают неожиданно влиять друг на друга.
1
Please open Telegram to view this post
VIEW IN TELEGRAM
1
Развёрнутое пояснение

Здесь три строки кода, но логика та же, что и в распределённых кешах: ограниченная ёмкость, политика вытеснения, цена промаха.

Отличие в том, что functools.lru_cache реализует строгий LRU, а, например, Redis с политикой allkeys-lru — приближённый: он сэмплирует несколько ключей и вытесняет наименее свежий из выборки, потому что честный LRU на миллионах ключей слишком дорог по памяти.

Почему это важно

Такие компромиссы и есть предмет архитектурных решений: что вытеснять, как инвалидировать, что случится с базой, когда тысячи запросов промахнутся одновременно.

💻 В Практикуме PRO есть курс «Архитектура программного обеспечения», и там целый модуль посвящён кешированию, мониторингу и адаптации к нагрузке. Изучите паттерны серверного и HTTP-кеширования, стратегии инвалидации кеша, а также Backpressure и Circuit Breaker для защиты системы под нагрузкой.

Весь курс идёт 6 месяцев и включает 11 проектов на основе реальных архитектур. Входные требования высокие: вам понадобится опыт от двух лет коммерческой разработки и уверенные знания сразу в нескольких областях, чтобы понимать, что происходит.

Чтобы принять решение, можно посмотреть бесплатный модуль и пройти входное тестирование:
https://tprg.ru/StFQ

Реклама. Рекламодатель: АНО ДПО "Образовательные технологии Яндекса" ИНН 7704282033, erid: 2W5zFHRceHw
Please open Telegram to view this post
VIEW IN TELEGRAM
2
Please open Telegram to view this post
VIEW IN TELEGRAM
3
Python: задачки и вопросы
Развёрнутое пояснение:

1. Вызывается dict.fromkeys с двумя аргументами: список ключей и один пустой список-значение.

2. Метод создаёт словарь, но не копирует значение для каждого ключа: api, db и cache получают ссылку на один и тот же список.

3. Обращение status['api'] возвращает этот общий список, и append('ok') изменяет его.

4. Поскольку db и cache указывают на тот же объект, их значения тоже становятся ['ok'].

5. print(status) выводит словарь, где каждый ключ содержит ['ok'].

Почему это важно: такая инициализация часто встречается при создании словарей статусов, группировок или конфигов с изменяемыми значениями. Вместо dict.fromkeys(keys, []) следует использовать dict comprehension {k: [] for k in keys} или collections.defaultdict(list), чтобы каждый ключ имел свой независимый список.
5
Please open Telegram to view this post
VIEW IN TELEGRAM
1
Python: задачки и вопросы
Развёрнутое пояснение:

1. Создаётся deque с maxlen=3: это кольцевой буфер, который хранит не более трёх элементов.

2. Цикл добавляет строки от 'a' до 'e'. Первые три ('a', 'b', 'c') помещаются полностью.

3. При добавлении 'd' буфер переполнен, и самый старый элемент слева ('a') автоматически вытесняется: остаётся ['b', 'c', 'd'].

4. При добавлении 'e' аналогично вытесняется 'b': буфер равен ['c', 'd', 'e'].

5. appendleft('f') вставляет элемент слева. Вытеснение всегда происходит с противоположного от вставки конца, поэтому удаляется самый правый элемент 'e'.

6. Итоговый буфер: ['f', 'c', 'd'], его и печатает print(list(recent)).

Почему это важно: deque с maxlen — стандартный способ хранить последние N записей без ручной обрезки: буфер свежих строк лога, история последних запросов, скользящее окно метрик. Ошибка в понимании того, с какого конца вытесняются элементы, приводит к тому, что в алертах и отладке теряются именно те записи, которые нужны: например, appendleft удобен для «самое свежее слева», но молча выбрасывает элементы справа, и если код дальше читает буфер с другого конца, данные «исчезают» без какого-либо исключения.
2
Please open Telegram to view this post
VIEW IN TELEGRAM
3
Python: задачки и вопросы
Развёрнутое пояснение:

1. partial(collect, []) создаёт объект add, в котором аргумент acc уже связан с одним объектом списка [].

2. print(add(1), add(2)) сначала вычисляет оба аргумента.

3. add(1) вызывает collect([], 1): в список добавляется 1, возвращается тот же список [1].

4. add(2) вызывает collect([1], 2): в этот же список добавляется 2, возвращается уже [1, 2].

5. print получает две ссылки на один и тот же список [1, 2] и печатает "[1, 2] [1, 2]".

Почему это важно: functools.partial часто используют для фиксации аргументов, но если такой аргумент изменяемый, все вызовы делят общее состояние. Это стреляет в фабриках обработчиков, логгерах и callback'ах, где частично применённый изменяемый объект оказывается общим для разных вызовов.
1
Please open Telegram to view this post
VIEW IN TELEGRAM
2