Python: задачки и вопросы
前往频道在 Telegram
Вопросы и задачки для подготовки к собеседованиям и прокачки навыков Разместить рекламу: @tproger_sales_bot Правила общения: https://tprg.ru/rules Другие каналы: @tproger_channels Другие наши проекты: https://tprg.ru/media
显示更多6 963
订阅者
+124 小时
-67 天
-6130 天
帖子存档
Главная идея: в конструкции try/except/finally блок finally выполняется всегда — и перед выходом из функции, и при исключениях, и даже если в try уже выполнен return. При этом return внутри finally имеет приоритет и заменяет любое ранее вычисленное значение возврата.
Что происходит внутри 𝚏𝚘𝚘():
🔘Заходим в блок 𝚝𝚛𝚢, выполняется 𝚛𝚎𝚝𝚞𝚛𝚗 "𝚝𝚛𝚢" — Python запоминает, что функция должна вернуть "try", но перед реальным выходом обязан выполнить 𝚏𝚒𝚗𝚊𝚕𝚕𝚢.
🔘Заходим в блок 𝚏𝚒𝚗𝚊𝚕𝚕𝚢:
▸ 𝚙𝚛𝚒𝚗𝚝("𝚏𝚒𝚗𝚊𝚕𝚕𝚢") — на экран выводится первая строка finally.
▸ 𝚛𝚎𝚝𝚞𝚛𝚗 "𝚏𝚒𝚗𝚊𝚕𝚕𝚢" — это новое значение возврата, которое перезаписывает ранее подготовленный 𝚛𝚎𝚝𝚞𝚛𝚗 "𝚝𝚛𝚢".
В итоге 𝚏𝚘𝚘() возвращает "finally".
Что печатает 𝚙𝚛𝚒𝚗𝚝:
🔘Внутри 𝚏𝚘𝚘(): печатается finally.
🔘Снаружи: 𝚙𝚛𝚒𝚗𝚝(𝚏𝚘𝚘()) печатает возвращённое значение "finally".
Итоговый вывод в терминале: finally finally. То есть первая строка — побочный эффект из 𝚏𝚒𝚗𝚊𝚕𝚕𝚢, вторая — результат возврата функции.
Почему это важно
🔘𝚏𝚒𝚗𝚊𝚕𝚕𝚢 всегда выполняется: при нормальном завершении, при return, при исключении.
🔘𝚛𝚎𝚝𝚞𝚛𝚗 внутри 𝚏𝚒𝚗𝚊𝚕𝚕𝚢 перетирает результат 𝚝𝚛𝚢/𝚎𝚡𝚌𝚎𝚙𝚝, поэтому так писать опасно: легко скрыть реальные ошибки или неожиданные возвраты.
🔘Рекомендуется не использовать 𝚛𝚎𝚝𝚞𝚛𝚗 в 𝚏𝚒𝚗𝚊𝚕𝚕𝚢; лучше ограничиваться чистым cleanup — закрытием файлов, соединений и т.п.
Сегодня попробуем новый формат: вопрос без правильного ответа. Творческий, где есть место рассуждениям вслух.
Допустим, вы создаёте библиотеку для удобного поиска файлов и каких-то действий с ними. Например, надо найти в директории
C:\logs все файлы больше 10 МБ с раcширением .log и не старше недели. Если использовать стандартные os.stat и datetime, то получается громоздко.
Задача — сделать это как можно более красиво, лаконично и pythonic way.
Некоторые неплохие варианты можно посмотреть в этом посте. Кстати, тоже канал по Python, который я веду лично.
Свои варианты интерфейса для обхода файлов с фильтрами кидайте в комментарии. Вместе обсудим плюсы и минусы, будет такая небольшая практика на выходных.Главный трюк этой конструкции в том, что else здесь относится не к if, а именно к циклу for. Цикл с else в Python можно читать так: для ... если цикл отработал до конца без break, то выполни ...
Разберём код построчно:
🔘Старт цикла: 𝚏𝚘𝚛 𝚒 𝚒𝚗 𝚛𝚊𝚗𝚐𝚎(𝟹): — значения 𝚒 будут 𝟶, 𝟷, 𝟸 по очереди.
🔘Первая итерация: 𝚒 = 𝟶 → выполняется 𝚙𝚛𝚒𝚗𝚝(𝚒, 𝚎𝚗𝚍=' '), на экран уходит 0 .Условие 𝚒 == 𝟷 ложно, 𝚋𝚛𝚎𝚊𝚔 не выполняется, цикл спокойно переходит к следующему значению.
🔘Вторая итерация: 𝚒 = 𝟷 → снова 𝚙𝚛𝚒𝚗𝚝(𝚒, 𝚎𝚗𝚍=' '), теперь на экране 0 1. Условие 𝚒 == 𝟷 становится истинным, выполняется 𝚋𝚛𝚎𝚊𝚔, и цикл немедленно прерывается изнутри, не доходя до конца диапазона.
🔘Что происходит с else:
Блок
𝚎𝚕𝚜𝚎:
𝚙𝚛𝚒𝚗𝚝("𝚎𝚕𝚜𝚎", 𝚎𝚗𝚍=' ')
выполняется только если цикл завершился «нормально» — то есть перебрал все элементы 𝚛𝚊𝚗𝚐𝚎(𝟹) без выхода через 𝚋𝚛𝚎𝚊𝚔. Здесь цикл прерывается на 𝚒 = 𝟷, поэтому 𝚎𝚕𝚜𝚎 пропускается.
🔘Завершение программы: После цикла всегда выполняется 𝚙𝚛𝚒𝚗𝚝("𝚍𝚘𝚗𝚎"), поэтому к уже напечатанному 0 1 добавляется done, итого вывод: 0 1 done.
Как про это думать
Полезная ментальная модель: цикл for … else работает почти как «поиск с флагом»:
🔘Если внутри цикла нашлось «что‑то особенное» и вы сделали 𝚋𝚛𝚎𝚊𝚔, 𝚎𝚕𝚜𝚎 не выполняется.
🔘Если вы честно прошли все элементы и так и не сделали 𝚋𝚛𝚎𝚊𝚔, тогда срабатывает 𝚎𝚕𝚜𝚎 — как ветка «ничего не нашли».
Бро, ты можешь тут реализоваться и т.д.
Став частью ОТП Банка, именно ты сделаешь сильнее всю команду! Расти, учись и пробуй новое — это твой шанс создать что-то по-настоящему крутое.
Присоединяйся к ребятам и делись роликом с теми, кто тоже готов к переменам 🚀
Это пример создания рекурсивного списка — структуры данных, которая ссылается сама на себя. Python позволяет создавать такие объекты благодаря механизму ссылок.
Важен порядок выполнения цепочки присваиваний.
В Python цепочка присваиваний a = b = c = значение выполняется так:
🔘Сначала вычисляется самое правое выражение (создаётся объект).
🔘Затем присваивания происходят слева направо для каждой цели.
Для кода Матрешка = Матрешка[0] = ['Матрешка'] выполнение идёт так
Шаг 1: Вычисляется правая часть
Создаётся список ['Матрешка'] — один объект в памяти
Шаг 2: Первое присваивание (слева)
Матрешка = ['Матрешка'] — переменная Матрешка получает ссылку на этот список
Шаг 3: Второе присваивание
Матрешка[0] = ['Матрешка'] — теперь Матрешка уже определена (из шага 2!), и мы можем обратиться к её первому элементу. Первый элемент (строка 'Матрешка') заменяется ссылкой на весь тот же список.
Ключевой момент
Когда выполняется Матрешка[0] = ..., переменная Матрешка уже существует — она была создана на предыдущем шаге цепочки. Поэтому мы можем обратиться к её элементу и заменить строку 'Матрешка' на ссылку на весь список.
Результат: список, который содержит сам себя в качестве первого элемента — рекурсивная структура [[...]].
Подводные камни:
🔘Такие структуры нельзя корректно сериализовать в JSON.
🔘Наивное копирование создаст проблемы — нужен 𝚌𝚘𝚙𝚢.𝚍𝚎𝚎𝚙𝚌𝚘𝚙𝚢 с обработкой циклов.
🔘Обход без проверки на посещённые узлы приведёт к бесконечной рекурсии.
Уже сегодня через несколько часов начнется конференция «Проектная исповедь» — о том, как жить, работать и ошибаться в мире дилемм.
Всего вас ждет 8 выступлений, кратко расскажем, что к чему
12:10 — Сергей Кожемякин поведает "Исповедь контрол‑фрика: как подчинить тревожность и использовать её в работе"
12:35 — Анна Аксенова поделится, как "Создать из хаоса порядок, или как сверить несверяемое"
13:00 — Алексей Мостовщиков и Андрей Лупий дадут инструкцию по применению GR в ИТ
13:25 — Яна Федорова расскажет, как "Промолчать ради гармонии или говорить о проблемах"
13:50 — Александр Ряховский поможет найти баланс контроля и вдохновения на выступлении "Искусство управлять невозможным"
14:15 — Дмитрий Урузгалиев построит "Мосты вместо стен: как соединить работу и жизнь, не сгорев на границе"
14:40 — Алексей Шершнев объяснит, "Что на самом деле убивает ИТ‑проекты"
15:05 — Иван Москвин покажет, как "Кастомизировать продукт под каждого или делать универсальное решение"
Конференция бесплатная, но надо зарегистрироваться. Сделать это можно на красивом лендинге: https://tprg.ru/KkLb
Это #партнёрский пост
Оператор * в множественном присваивании собирает «остатки» последовательности в список. Он работает как жадный захват всех элементов между фиксированными переменными.
Разбор по шагам:
🔘𝚡 = [𝟷, 𝟸, 𝟹, 𝟺, 𝟻] # список из 5 элементов
🔘𝚊, *𝚋, 𝚌 = 𝚡 # распаковка с остатком:
a берёт первый элемент: a = 1,
c берёт последний элемент: c = 5
*b собирает всё, что между: b = [2, 3, 4]
🔘𝚙𝚛𝚒𝚗𝚝(𝚕𝚎𝚗(𝚋)) # 3
Как работает распаковка
🔘Переменные без * получают ровно один элемент.
🔘Переменная с * захватывает всё оставшееся (может быть пустым списком).
🔘* можно использовать только один раз в присваивании.
Примеры разных случаев
𝚡 = [𝟷, 𝟸]
*𝚊, 𝚋 = 𝚡 # a = 1, b = 2
𝚢 = [𝟷]
*𝚌, 𝚍 = 𝚢 # c = [ ], d = 1 (остаток пустой!)
𝚣 = [𝟷, 𝟸, 𝟹]
𝚎, *𝚏 = 𝚣 # e = 1, f = [2, 3]
Зачем нужно такое поведение
🔘Элегантно обрабатывать последовательности переменной длины.
🔘Частый паттерн: 𝚏𝚒𝚛𝚜𝚝, *𝚛𝚎𝚜𝚝 = 𝚕𝚒𝚜𝚝 для разделения головы и хвоста.
🔘Работает с любыми итерируемыми объектами: списками, кортежами, строками.
Подводные камни
🔘Если элементов меньше, чем фиксированных переменных, получите ValueError.
🔘* всегда создаёт список, даже если элементов нет.
🔘Можно игнорировать остаток через : 𝚊, *, 𝚋 = [𝟷, 𝟸, 𝟹, 𝟺] → игнорируем середину.
Оператор += для списков работает in-place (изменяет объект на месте), в отличие от обычной конкатенации +, которая создаёт новый список.
Разбор по шагам:
🔘𝚊 = создаёт список.
🔘𝚋 = 𝚊 создаёт новую ссылку на тот же список; 𝚊 и 𝚋 указывают на один объект в памяти.
🔘𝚊 += вызывает метод 𝚊.𝚎𝚡𝚝𝚎𝚗𝚍() — расширяет существующий список, добавляя элемент 3. Объект не меняется, изменяется его содержимое.
🔘Поскольку 𝚋 указывает на тот же объект, изменения видны и через 𝚋.
🔘𝚊 == 𝚋 → True (значения равны), 𝚊 𝚒𝚜 𝚋 → True (это один и тот же объект).
Сравнение с обычным +
Если бы использовали 𝚊 = 𝚊 + [3], поведение было бы другим. Создавался бы новый список 𝚊. Переменная 𝚋 продолжала бы указывать на старый список , поэтому 𝚊 == 𝚋 → False, 𝚊 𝚒𝚜 𝚋 → False.
Почему += работает in-place
Для изменяемых типов (списки, множества, словари) оператор += оптимизирован: вместо создания нового объекта он модифицирует существующий. Для неизменяемых типов (строки, кортежи, числа) += всегда создаёт новый объект. Аналогично ведёт себя *=.
