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

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

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

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

نمایش بیشتر
7 131
مشترکین
-124 ساعت
+77 روز
اطلاعاتی وجود ندارد30 روز
آرشیو پست ها
Давайте разберём пошагово: 1️⃣𝚡 = [𝟷, 𝟸, 𝟹] — создаётся список. 2️⃣𝚢 = 𝚡 — теперь 𝚢 и 𝚡 указывают на один и тот же объект списка [𝟷, 𝟸, 𝟹]. 3️⃣𝚡, 𝚢[𝟶] = 𝚢, 𝟺 — здесь начинается хитрость множественного присваивания. Python сначала вычисляет правую часть целиком: создаётся временный кортеж из текущих значений 𝚢 и 𝟺, то есть [𝟷, 𝟸, 𝟹] и 𝟺.​ Только после этого начинается присваивание слева направо: 4️⃣𝚡 = [𝟷, 𝟸, 𝟹] — переменная 𝚡 теперь указывает на тот же список, что и раньше (потому что 𝚢 указывал на него). 5️⃣𝚢[𝟶] = 𝟺 — модифицируется первый элемент списка, на который указывает 𝚢. Но поскольку 𝚡 и 𝚢 указывают на один и тот же список, изменение 𝚢[𝟶] сразу видно и через 𝚡. В итоге список становится [𝟺, 𝟸, 𝟹], и обе переменные выводят его.​ Эта задачка показывает важную особенность: порядок вычисления и присваивания в множественном присваивании может приводить к неочевидным результатам, когда левая и правая части «переплетены» через ссылки на изменяемые объекты.

Что выведет код?
Anonymous voting

В пятницу не стал жестить, большинство справились с задачей. Разбор по шагам: 1️⃣Определяется функция‑генератор: 𝚍𝚎𝚏 𝚏(): 𝚢𝚒𝚎𝚕𝚍 "𝚊" 𝚢𝚒𝚎𝚕𝚍 "𝚋" 2️⃣Вызов: 𝚜 = ":".𝚓𝚘𝚒𝚗(𝚏()) — здесь берётся встроенный метод 𝚓𝚘𝚒𝚗, разделителем служит строка ":", а аргументом — генератор.​ Генератор по очереди выдаёт "a", потом "b". 3️⃣Метод join работает так: — «Собери все строки из генератора, вставь между ними разделитель»​ — Результат — строка "a:b". 4️⃣Вывод: 𝚙𝚛𝚒𝚗𝚝(𝚜) → 'a:b'​ Почему это важно 🔘join очень полезен: работает с любым итерируемым объектом: списком, кортежем, даже генератором. 🔘Передавая генератор, можно собирать строки «на лету» без лишней памяти и промежуточных списков. 🔘Если передать нестроковые элементы, будет TypeError, но для генератора, выдающего строки — всё корректно.

Что выведет код?
Anonymous voting

Главный подвох: Python использует late binding в замыканиях — переменные захватываются по ссылке, и их значение берётся в момент вызова функции, а не в момент создания.​ Разбор по шагам в псевдо‑записи: 🔘Создание списка функций: 𝚏𝚞𝚗𝚌𝚜 = 𝚏𝚘𝚛 𝚒 𝚒𝚗 𝚛𝚊𝚗𝚐𝚎(𝟸): ▸ 𝚏𝚞𝚗𝚌𝚜.𝚊𝚙𝚙𝚎𝚗𝚍(𝚕𝚊𝚖𝚋𝚍𝚊 𝚡: 𝚡 * 𝚒) — создаётся три лямбды, каждая ссылается на переменную i из внешнего scope. — переменная i меняется: 0 → 1 → 2.​ 🔘Момент вызова функций: Когда выполняется [f(1) for f in funcs], цикл for i in range(3) уже завершился.​ Переменная i в глобальном scope теперь равна 2 (последнее значение).​ 🔘Выполнение каждой лямбды: Первый вызов: funcs[0](1) → lambda x: x * i, где i = 2 → 1 * 2 = 2.​ Второй вызов: funcs[1](1) → та же лямбда, та же ссылка на i, i = 2 → 1 * 2 = 2.​ Третий вызов: funcs[2](1) → опять i = 2 → 1 * 2 = 2.​ 🔘Результат: [2, 2, 2].​ Чтобы каждая лямбда «запомнила» своё значение i, нужно захватить его через default argument: funcs = [lambda x, i=i: x * i for i in range(3)] print([f(1) for f in funcs]) # [0, 1, 2] Здесь i=i создаёт локальную переменную внутри лямбды с значением на момент создания, а не ссылкой на внешнюю переменную.​ Почему это важно 🔘Late binding — частая причина ошибок в циклах с lambda, list comprehension и callback‑функциями.​ 🔘Правило: если нужно захватить переменную из цикла, всегда используйте default argument для lambda или локальную переменную внутри comprehension.​ 🔘Это поведение отличается от некоторых других языков, где захват происходит по значению по умолчанию.

Что выведет код?
Anonymous voting

Главный подвох: обычно в Python 3 переменные цикла внутри list comprehension локальны и не «утекают» наружу, но walrus operator := специально создаёт или перезаписывает переменную в вмещающей области видимости.​ Разбор по шагам в псевдо‑записи: 🔘Инициализация: 𝚡 = 𝟷𝟶 — внешняя переменная равна 10.​ 🔘List comprehension с walrus: 𝚗𝚞𝚖𝚜 = 𝚡 := 𝚒 𝚏𝚘𝚛 𝚒 𝚒𝚗 𝚛𝚊𝚗𝚐𝚎(𝟸) — 𝚒 = 𝟶 → 𝚡 := 𝟶 (перезаписывает внешний 𝚡, теперь 𝚡 = 0) — 𝚒 = 𝟷 → 𝚡 := 𝟷 (перезаписывает внешний 𝚡, теперь 𝚡 = 1) — 𝚒 = 𝟸 → 𝚡 := 𝟸 (перезаписывает внешний 𝚡, теперь 𝚡 = 2) Результат: 𝚗𝚞𝚖𝚜 = 𝟶, 𝟷, 𝟸, а внешний 𝚡 = 𝟸.​ 🔘Вывод: 𝚙𝚛𝚒𝚗𝚝(𝚡, 𝚗𝚞𝚖𝚜) → печатает 2 [0, 1, 2].​ Сравнение с обычным list comprehension > x = 10 > nums = [x for x in range(3)] > print(x, nums) Здесь вывод: 10 [0, 1, 2], потому что переменная x внутри comprehension локальна и не затрагивает внешнюю переменную.​ Почему это важно 🔘Walrus operator := создан для присваивания внутри выражений, но его «утечка» в outer scope может быть неожиданной.​ 🔘В продакшен‑коде лучше использовать := осторожно, чтобы не перезаписать случайно важные переменные.​ 🔘Для чистого list comprehension без побочных эффектов используйте обычную переменную цикла, а не :=.

Что выведет код?
Anonymous voting

Главный подвох здесь в том, что переменная цикла num остаётся доступной после цикла и сохраняет последнее значение из итерируемой последовательности.​ Разбор по шагам: 🔘Создание исходных данных: 𝚗𝚞𝚖𝚜 = 𝟷, 𝟸, 𝟹, 𝟺, 𝟻, 𝟼, 𝟽, 𝟾, 𝟿 # список чисел от 1 до 9.​ 𝚗𝚞𝚖𝚜_𝚎𝚟𝚎𝚗 = [] # пустой список для чётных чисел.​ 🔘Цикл фильтрации: 𝚏𝚘𝚛 𝚗𝚞𝚖 𝚒𝚗 𝚗𝚞𝚖𝚜: # перебираем каждое число.​ ▸ 𝚒𝚏 𝚗𝚘𝚝 𝚗𝚞𝚖 % 𝟸: # проверяем чётность (остаток от деления на 2 равен 0).​ ▸▸ 𝚗𝚞𝚖𝚜_𝚎𝚟𝚎𝚗.𝚊𝚙𝚙𝚎𝚗𝚍(𝚗𝚞𝚖) # добавляем чётные числа: 𝟸, 𝟺, 𝟼, 𝟾.​ После цикла: 𝚗𝚞𝚖𝚜_𝚎𝚟𝚎𝚗 = 𝟸, 𝟺, 𝟼, 𝟾 # четыре элемента.​ 𝚗𝚞𝚖 = 𝟿 # переменная цикла сохраняет последнее значение из 𝚗𝚞𝚖𝚜.​ 🔘Попытка удаления: 𝚗𝚞𝚖𝚜_𝚎𝚟𝚎𝚗.𝚙𝚘𝚙(𝚗𝚞𝚖) # пытаемся вызвать pop(9).​ Метод list.pop(index) принимает индекс, а не значение.​ Список имеет длину 4, индексы от 𝟶 до 𝟹, поэтому индекс 𝟹 выходит за границы и вызывает IndexError: pop index out of range.​ 🔘Почему это неочевидно: Вообще говоря pop() удаляет последний элемент, если не указан индекс, но здесь указан индекс 𝚗𝚞𝚖 = 𝟿.​ Переменная 𝚗𝚞𝚖 после цикла не «сбрасывается», а сохраняет последнее значение, что часто приводит к ошибкам.​ — Если нужно удалить последний элемент, следует использовать 𝚗𝚞𝚖𝚜_𝚎𝚟𝚎𝚗.𝚙𝚘𝚙() без аргументов.​ — Если нужно удалить по значению, следует использовать 𝚗𝚞𝚖𝚜_𝚎𝚟𝚎𝚗.𝚛𝚎𝚖𝚘𝚟𝚎(𝚗𝚞𝚖).​ Кратко выводы Правило: переменные цикла for в Python не локальны внутри цикла, они остаются в той же области видимости, где был цикл.​ После цикла for num in nums: переменная num доступна и равна последнему элементу из nums.​ Это часто приводит к ошибкам, если забыть, что num сохранилась, и использовать её в коде после цикла.

Главный подвох здесь в том, что переменная цикла num остаётся доступной после цикла и сохраняет последнее значение из итерируемой последовательности.​ Разбор по шагам: 🔘Создание исходных данных: 𝚗𝚞𝚖𝚜 = 𝟷, 𝟸, 𝟹, 𝟺, 𝟻, 𝟼, 𝟽, 𝟾, 𝟿 # список чисел от 1 до 9.​ 𝚗𝚞𝚖𝚜_𝚎𝚟𝚎𝚗 = [] # пустой список для чётных чисел.​ 🔘Цикл фильтрации: 𝚏𝚘𝚛 𝚗𝚞𝚖 𝚒𝚗 𝚗𝚞𝚖𝚜: — перебираем каждое число.​ ▸ 𝚒𝚏 𝚗𝚘𝚝 𝚗𝚞𝚖 % 𝟸: — проверяем чётность (остаток от деления на 2 равен 0).​ ▸▸ 𝚗𝚞𝚖𝚜_𝚎𝚟𝚎𝚗.𝚊𝚙𝚙𝚎𝚗𝚍(𝚗𝚞𝚖) — добавляем чётные числа: 𝟸, 𝟺, 𝟼, 𝟾.​ После цикла: 𝚗𝚞𝚖𝚜_𝚎𝚟𝚎𝚗 = 𝟸 , 𝟺 , 𝟼 , 𝟾 2,4,6,8 — четыре элемента.​ 𝚗𝚞𝚖 = 𝟿 — переменная цикла сохраняет последнее значение из 𝚗𝚞𝚖𝚜.​ Попытка удаления: 𝚗𝚞𝚖𝚜_𝚎𝚟𝚎𝚗.𝚙𝚘𝚙(𝚗𝚞𝚖) — пытаемся вызвать pop(9).​ Метод list.pop(index) принимает индекс, а не значение.​ Список имеет длину 4, индексы от 𝟶 до 𝟹, поэтому индекс 𝟹 выходит за границы и вызывает IndexError: pop index out of range.​ Почему это неочевидно: Многие думают, что pop() удаляет последний элемент, если не указан индекс, но здесь указан индекс 𝚗𝚞𝚖 = 𝟿.​ Переменная 𝚗𝚞𝚖 после цикла не «сбрасывается», а сохраняет последнее значение, что часто приводит к ошибкам.​ Если нужно удалить последний элемент, следует использовать 𝚗𝚞𝚖𝚜\_𝚎𝚟𝚎𝚗.𝚙𝚘𝚙() без аргументов.​ Если нужно удалить по значению, следует использовать 𝚗𝚞𝚖𝚜\_𝚎𝚟𝚎𝚗.𝚛𝚎𝚖𝚘𝚟𝚎(𝚗𝚞𝚖).​ Как про это думать Правило: переменные цикла for в Python не локальны внутри цикла, они остаются в той же области видимости, где был цикл.​ После цикла for num in nums: переменная num доступна и равна последнему элементу из nums.​ Это часто приводит к ошибкам, если забыть, что num сохранилась, и использовать её в коде после цикла.

Что выведет код?
Anonymous voting

Главная идея: в конструкции try/except/finally блок finally выполняется всегда — и перед выходом из функции, и при исключениях, и даже если в try уже выполнен return. При этом return внутри finally имеет приоритет и заменяет любое ранее вычисленное значение возврата. Что происходит внутри 𝚏𝚘𝚘(): 🔘Заходим в блок 𝚝𝚛𝚢, выполняется 𝚛𝚎𝚝𝚞𝚛𝚗 "𝚝𝚛𝚢" — Python запоминает, что функция должна вернуть "try", но перед реальным выходом обязан выполнить 𝚏𝚒𝚗𝚊𝚕𝚕𝚢. 🔘Заходим в блок 𝚏𝚒𝚗𝚊𝚕𝚕𝚢: ▸ 𝚙𝚛𝚒𝚗𝚝("𝚏𝚒𝚗𝚊𝚕𝚕𝚢") — на экран выводится первая строка finally. ▸ 𝚛𝚎𝚝𝚞𝚛𝚗 "𝚏𝚒𝚗𝚊𝚕𝚕𝚢" — это новое значение возврата, которое перезаписывает ранее подготовленный 𝚛𝚎𝚝𝚞𝚛𝚗 "𝚝𝚛𝚢". В итоге 𝚏𝚘𝚘() возвращает "finally". Что печатает 𝚙𝚛𝚒𝚗𝚝: 🔘Внутри 𝚏𝚘𝚘(): печатается finally. 🔘Снаружи: 𝚙𝚛𝚒𝚗𝚝(𝚏𝚘𝚘()) печатает возвращённое значение "finally". Итоговый вывод в терминале: finally finally. То есть первая строка — побочный эффект из 𝚏𝚒𝚗𝚊𝚕𝚕𝚢, вторая — результат возврата функции. Почему это важно 🔘𝚏𝚒𝚗𝚊𝚕𝚕𝚢 всегда выполняется: при нормальном завершении, при return, при исключении. 🔘𝚛𝚎𝚝𝚞𝚛𝚗 внутри 𝚏𝚒𝚗𝚊𝚕𝚕𝚢 перетирает результат 𝚝𝚛𝚢/𝚎𝚡𝚌𝚎𝚙𝚝, поэтому так писать опасно: легко скрыть реальные ошибки или неожиданные возвраты. 🔘Рекомендуется не использовать 𝚛𝚎𝚝𝚞𝚛𝚗 в 𝚏𝚒𝚗𝚊𝚕𝚕𝚢; лучше ограничиваться чистым cleanup — закрытием файлов, соединений и т.п.

Что выведет код?
Anonymous voting