Python: задачки и вопросы
Open in Telegram
Вопросы и задачки для подготовки к собеседованиям и прокачки навыков Разместить рекламу: @tproger_sales_bot Правила общения: https://tprg.ru/rules Другие каналы: @tproger_channels Другие наши проекты: https://tprg.ru/media
Show more7 130
Subscribers
+424 hours
+57 days
+130 days
Posts Archive
Канал по нейронкам, который часто пересекается с Python по темам: @neuro_channel
Веду его тоже я, так что если вам нравятся задачки здесь, то буду рад видеть вас и в «Нейроканале». Это всё часть медиа Tproger.
Три примечательных поста:
1️⃣Про утилиту, которая автоматически снимает цензуру (safety alignment) с трансформерных языковых моделей без дообучения и ручного тюнинга.
2️⃣Разбор сборки GPT‑OSS с нуля на чистом Python, без PyTorch и без GPU: последовательно с объяснениями от Softmax и RMSNorm до Grouped Query Attention.
3️⃣Обзор трендов Hugging Face за неделю с супер-кратким описанием моделей, выходит регулярно.
Плюс новости о выходе новых нейронок, чтобы не пропустить ничего важного. Все свежие громкие релизы, чтобы вы могли читать только один канал. Иногда получается даже раньше официального запуска, чем я особенно горжусь :)
Заходите и подписывайтесь: @neuro_channel
Развёрнутое пояснение
1️⃣Список 𝚊 = [𝟷, 𝟸, 𝟹, 𝟺, 𝟻]. Индекс −𝟹 соответствует элементу 𝟹 (третий с конца), а индекс 𝟶 — элементу 𝟷 (первый).
2️⃣Срез [𝚜𝚝𝚊𝚛𝚝:𝚜𝚝𝚘𝚙] идёт слева направо по умолчанию. Позиция −𝟹 (элемент 𝟹) находится правее позиции 𝟶 (элемент 𝟷).
3️⃣Поскольку 𝚜𝚝𝚊𝚛𝚝 > 𝚜𝚝𝚘𝚙 при движении слева направо, срез не захватывает ни одного элемента и возвращает [].
4️⃣Чтобы получить последние три элемента, нужно писать 𝚊[-𝟹:] (без указания конца) или 𝚊[-𝟹:𝙽𝚘𝚗𝚎].
Почему это важно
Ошибка [-𝚗:𝟶] вместо [-𝚗:] — частая ловушка при динамическом формировании срезов; результат всегда пуст, но ошибки не выбрасывается, и баг может долго оставаться незамеченным.
Развёрнутое пояснение
1️⃣В словаре 𝚍 два ключа: 𝟷 и 𝟸; соответствующие значения — "𝚘𝚗𝚎" и "𝚝𝚠𝚘".
2️⃣Выражение 𝟷 𝚒𝚗 𝚍 проверяет наличие 𝟷 среди ключей словаря; такой ключ есть, поэтому результат — True.
3️⃣Выражение "𝚘𝚗𝚎" 𝚒𝚗 𝚍 тоже смотрит только на ключи: строка "𝚘𝚗𝚎" не является ключом, это значение; поэтому результат — False.
4️⃣Функция 𝚙𝚛𝚒𝚗𝚝(𝟷 𝚒𝚗 𝚍, "𝚘𝚗𝚎" 𝚒𝚗 𝚍) выводит True False.
Почему это важно
Путаница между проверкой по ключам и по значениям часто ломает валидацию и условия: чтобы искать по значениям, нужно явно использовать "𝚘𝚗𝚎" 𝚒𝚗 𝚍.𝚟𝚊𝚕𝚞𝚎𝚜() или итерироваться по парам, а не надеяться на поведение по умолчанию.
Развёрнутое пояснение
🔘Список 𝚗𝚞𝚖𝚜 = [𝟸, 𝟺, 𝟼, 𝟾]. Цикл использует внутренний индекс, который увеличивается на каждой итерации.
0️⃣Шаг 𝟶: индекс 𝟶, элемент 𝟸, условие истинно, вызывается 𝚗𝚞𝚖𝚜.𝚛𝚎𝚖𝚘𝚟𝚎(𝟸). Список становится [𝟺, 𝟼, 𝟾], но индекс уже переходит к 𝟷.
1️⃣Шаг 𝟷: индекс 𝟷, в списке [𝟺, 𝟼, 𝟾] это элемент 𝟼 (а не 𝟺!), условие истинно, удаляем 𝟼. Список становится [𝟺, 𝟾], индекс переходит к 𝟸.
2️⃣Шаг 𝟸: индекс 𝟸 выходит за пределы списка длины 𝟸, цикл завершается.
3️⃣Элементы 𝟺 и 𝟾 никогда не были проверены, потому что итератор их «перепрыгнул». Результат: [𝟺, 𝟾].
Почему это важно
Задача наглядно показывает, почему нельзя удалять элементы из списка во время итерации по нему: даже если кажется, что «всё должно удалиться», половина элементов останется. Безопасные альтернативы: итерация по копии 𝚏𝚘𝚛 𝚡 𝚒𝚗 𝚗𝚞𝚖𝚜[:], list comprehension [𝚡 𝚏𝚘𝚛 𝚡 𝚒𝚗 𝚗𝚞𝚖𝚜 𝚒𝚏 𝚡 % 𝟸 != 𝟶], или проход в обратном порядке.
Развёрнутое пояснение
1️⃣При определении класса 𝙱𝚘𝚡 создаётся атрибут класса 𝚒𝚝𝚎𝚖𝚜 и привязывается к пустому списку []; этот список один на весь класс, а не на экземпляр.
2️⃣В конструкторе __init__ обращение 𝚜𝚎𝚕𝚏.𝚒𝚝𝚎𝚖𝚜 находит этот атрибут класса (у экземпляра ещё нет своего 𝚒𝚝𝚎𝚖𝚜) и вызывает у него метод 𝚊𝚙𝚙𝚎𝚗𝚍.
3️⃣Создание 𝚊 = 𝙱𝚘𝚡(𝟷) добавляет 𝟷 в общий список → теперь 𝚒𝚝𝚎𝚖𝚜 = [𝟷]. Создание 𝚋 = 𝙱𝚘𝚡(𝟸) добавляет 𝟸 в тот же самый список → [𝟷, 𝟸].
4️⃣И 𝚊.𝚒𝚝𝚎𝚖𝚜, и 𝚋.𝚒𝚝𝚎𝚖𝚜 ссылаются на один и тот же общий список, поэтому print(a.items, b.items) выводит [1, 2] [1, 2].
Почему это важно
Общий изменяемый атрибут класса легко превращается в неявный «shared state» между инстансами и рождает трудноуловимые баги; если список должен быть отдельным у каждого объекта, его нужно создавать в __init__ как 𝚜𝚎𝚕𝚏.𝚒𝚝𝚎𝚖𝚜 = [].
Весь год вы носили футболки и джинсы… Хватит!
Заглядывайте к нам в виртуальную примерочную и выбирайте себе идеальный скин для встречи Нового года!
Делитесь в комментариях, какой скин вам выпал 👀
Развёрнутое пояснение
1️⃣Создаётся список 𝚗𝚞𝚖𝚜 = [𝟷, 𝟸, 𝟹], переменная 𝚊 получает ссылку на тот же самый объект: и 𝚊, и 𝚗𝚞𝚖𝚜 указывают на один список.
2️⃣Выражение 𝚗𝚞𝚖𝚜[:] = [𝟺, 𝟻] — это именно срезовое присваивание: левая часть берёт весь список как срез, а правая заменяет его содержимое элементами из нового списка.
3️⃣В результате исходный объект списка переписывается на [𝟺, 𝟻], но ссылка 𝚊 по‑прежнему указывает на него же, так что и 𝚊, и 𝚗𝚞𝚖𝚜 теперь содержат один и тот же список [𝟺, 𝟻].
4️⃣𝚙𝚛𝚒𝚗𝚝(𝚊, 𝚗𝚞𝚖𝚜) печатает [4, 5] [4, 5].
Почему это важно
Потом что [:] = — это не то же самое, что простое =, и любое незаметное срезовое присваивание может поменять список, который уже разделяют несколько переменных или даже разные части кода.
Развёрнутое пояснение
1️⃣Переменной 𝚊 присваивается целое число 𝟷.
2️⃣В выражении 𝚋 = (𝚊) скобки играют только роль группировки — результатом остаётся то же значение 𝟷, и 𝚋 ссылается на обычное целое; поэтому 𝚝𝚢𝚙𝚎(𝚋) 𝚒𝚜 𝚝𝚞𝚙𝚕𝚎 возвращает False.
3️⃣В выражении 𝚌 = (𝚊,) уже есть запятая, которая и создаёт одноэлементный кортеж; поэтому 𝚝𝚢𝚙𝚎(𝚌) 𝚒𝚜 𝚝𝚞𝚙𝚕𝚎 даёт True.
4️⃣Функция 𝚙𝚛𝚒𝚗𝚝(𝚝𝚢𝚙𝚎(𝚋) 𝚒𝚜 𝚝𝚞𝚙𝚕𝚎, 𝚝𝚢𝚙𝚎(𝚌) 𝚒𝚜 𝚝𝚞𝚙𝚕𝚎) выводит False True одной строкой.
Почему это важно
Один «лишний» символ, легко превращает значение в кортеж и ломает код далеко от места ошибки (например, при работе с dict, URL, числами), поэтому одноэлементные кортежи всегда лучше записывать явно как (𝚟𝚊𝚕𝚞𝚎,), а не полагаться на неочевидный синтаксис.
Available now! Telegram Research 2025 — the year's key insights 
