Python: задачки и вопросы
7.01K 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
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.
1
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), чтобы каждый ключ имел свой независимый список.
4
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
1
Python: задачки и вопросы
Развёрнутое пояснение:

1. Создаётся список levels с тремя строками.

2. dict.fromkeys(levels, []) строит словарь, в котором каждый ключ получает не свой список, а одну и ту же ссылку на один пустой список.

3. Обращение buckets['warn'] возвращает тот же общий список.

4. Метод append('disk full') изменяет этот единственный список.

5. print(buckets) выводит словарь, где под всеми тремя ключами лежит уже изменённый список.

Почему это важно: при группировке событий, логов или метрик по категориям часто пишут buckets = dict.fromkeys(groups, []), ожидая независимые корзины. В результате запись в одну категорию молча заражает все остальные, что даёт трудноуловимый баг в аналитике и обработке потоков данных.
1
Please open Telegram to view this post
VIEW IN TELEGRAM
Python: задачки и вопросы
Развёрнутое пояснение:

1. retry(2) вызывается и возвращает функцию decorator.

2. Синтаксис
@retry(2) применяет decorator к функции fetch.

3. decorator создаёт функцию wrapper и возвращает её.

4. Итоговое имя fetch теперь ссылается на объект wrapper.

5. Внутренняя функция wrapper не обёрнута в functools.wraps(f), поэтому её атрибут __name__ остаётся 'wrapper'.

6. print(fetch.__name__) печатает wrapper.

Почему это важно: в production-коде декораторы с аргументами часто забывают обернуть внутреннюю функцию в functools.wraps. Это ломает логирование, метрики и отладку, потому что все обёрнутые функции начинают называться wrapper, а их __doc__ становится None. Правильный шаблон — добавить
@wraps(f) над wrapper.
Please open Telegram to view this post
VIEW IN TELEGRAM
2
Python: задачки и вопросы
Развёрнутое пояснение:

1. Создаётся deque с maxlen=3, то есть буфер фиксированного размера.\n2. В цикле append добавляются элементы a, b, c, d. Когда добавляется d, deque уже заполнен тремя элементами a, b, c, поэтому самый старый элемент a удаляется слева. После цикла внутри buf лежат b, c, d.\n3. Вызов buf.rotate(1) сдвигает элементы вправо на одну позицию: последний элемент d перемещается в начало, а b и c сдвигаются вправо. Порядок становится d, b, c.\n4. print(''.join(buf)) объединяет элементы в строку dbc.\n\nПочему это важно: deque с maxlen часто используется для скользящего окна последних событий, например при сборе последних логов или метрик. rotate нужен для циклических очередей и round-robin. Важно помнить, что rotate(1) двигает элементы вправо, rotate(-1) — влево, а maxlen автоматически вытесняет старые элементы с противоположного конца от добавления.
2