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

a * b умножает векторы поэлементно: первый элемент с первым, второй со вторым, третий с третьим. Результат — массив [4 10 18]. a @ b делает те же умножения и складывает их в одно число: 4 + 10 + 18 = 32. Это скалярное произведение, на нём построены линейная регрессия и косинусное сходство текстов.

Если перепутать операторы, код не упадёт: обе операции корректны для векторов одинаковой длины. Ошибка проявится только неправильными числами в результате, и заметить её можно только по смыслу этих чисел.

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

Задача проверяет, различаете ли вы две операции по смыслу: где нужно перемножить признаки поэлементно, а где — свернуть векторы в одно число.

💻 Если знаете Python, а с математикой за NumPy знакомы хуже — посмотрите курс «Математика для анализа данных» Практикума PRO. Скалярное произведение, нормы и работа с векторами в NumPy — это модуль линейной алгебры, дальше матанализ до градиентного спуска, теорвер и статистика до A/B-тестов. 1000+ задач, практика на Python в тренажёре, в конце — симулятор математической секции собеседования.

Реклама. Рекламодатель: АНО ДПО "Образовательные технологии Яндекса" ИНН 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
Please open Telegram to view this post
VIEW IN TELEGRAM
3
Please open Telegram to view this post
VIEW IN TELEGRAM