Python: задачки и вопросы
6.87K subscribers
1.35K photos
1 video
1 file
129 links
Вопросы и задачки для подготовки к собеседованиям и прокачки навыков

Разместить рекламу: @tproger_sales_bot

Правила общения: https://tprg.ru/rules

Другие каналы: @tproger_channels

Другие наши проекты: https://tprg.ru/media
Download Telegram
Поиск и вставка

1️⃣Записи rows упорядочены по первому числу. Функция key возвращает это число из каждой строки.

2️⃣bisect_left применяет key к элементам rows, но не к аргументу new. Поэтому при поиске сравниваются число и кортеж. Возникает TypeError, обработчик печатает search failed.

3️⃣insort_left при поиске позиции вычисляет key(new), получая 15. Число попадает между 10 и 20, и вставка успешно находит индекс 1.

4️⃣В список вставляется исходный кортеж, не число 15. Результат: [(10, 'a'), (15, 'b'), (20, 'c')].

Почему это важно
При ручном поиске позиции передавайте значение в пространстве ключей: bisect_left(rows, key(new), key=key). При вставке через insort передают целую запись. Похожие параметры этих функций не означают одинаковую обработку искомого объекта.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Два момента применения точности

1️⃣В локальном контексте ctx.prec = 3 задаёт три значащие цифры для операций Decimal. Это не число знаков после запятой.

2️⃣Decimal("1.2345") точно сохраняет переданную десятичную строку. Конструктор не обрезает её по prec, поэтому первый print выводит 1.2345.

3️⃣Выражение +price выполняет операцию Decimal в текущем контексте. При точности 3 результат округляется до 1.23.

4️⃣Второй print выводит 1.23. Исходный price не изменился: унарный плюс вернул новое значение.

Почему это важно
Точность контекста влияет на вычисления, а не на количество цифр, которое разрешено загрузить из строки. При расчётах цены различайте точность арифметики и округление до денежной единицы: для фиксированного числа знаков после запятой обычно задают явный quantize.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Порядок подготовки вызова

1️⃣Функция mark печатает переданную метку и возвращает ту же строку. Тело send печатает sent.

2️⃣Сначала готовятся позиционные аргументы, в том числе распакованные через *. Выражение [mark("item")] печатает item и даёт один позиционный аргумент.

3️⃣Затем вычисляется именованный аргумент label. Вызов mark("label") печатает label и возвращает его значение. Написанный левее аргумент здесь вычисляется позже.

4️⃣После подготовки аргументов вызывается send. Итоговые строки: item, label, sent. Такой вызов допустим: после именованного аргумента стоит распаковка *, а не обычный позиционный аргумент.

Почему это важно
Не рассчитывайте на визуальный порядок побочных эффектов в смешанном вызове с *. Если операции должны идти строго по порядку, вычислите их результаты отдельными инструкциями перед вызовом функции.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Куда делась запись

1️⃣В исходном словаре число 1 и строка "1" — разные ключи. Обе записи существуют, поэтому первый print выводит 2.

2️⃣При json.dumps числовой ключ преобразуется в строковый. В JSON появляются два одинаковых имени "1", сначала со значением number, затем со значением text.

3️⃣Стандартный json.loads принимает повторяющиеся имена и оставляет последнее значение. Получается словарь с одной парой: '1': 'text'.

4️⃣Последний print выводит число записей и значение строкового ключа: 1 text.

Почему это важно
Сохранение в JSON и обратное чтение не гарантируют сохранения структуры произвольного Python-словаря. Для идентификаторов выберите один тип ключей заранее и проверяйте коллизии при преобразовании. Если нужно сохранить типы ключей, можно передать записи списком пар с явным описанием типа.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Настройка на один вызов

1️⃣partial(print, sep="-") создаёт fmt с заранее заданным именованным аргументом sep.

2️⃣В первом вызове свой sep не передан. print использует дефис и выводит a-b.

3️⃣Во втором вызове sep="|" переопределяет сохранённое значение. print получает один sep и выводит a|b, ошибки нет.

4️⃣Это переопределение действует на конкретный вызов: само сохранённое значение в fmt не меняется. Следующий вызов без sep снова использовал бы дефис.

Почему это важно
Именованные аргументы partial можно переопределять при вызове. Для обработчиков это удобные настройки по умолчанию. Позиционные аргументы, связанные в partial, работают иначе: новые позиционные аргументы добавляются к ним.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Ноль, которого нет

1️⃣a хранит apple=2. У b дополнительно есть pear=0, у c — pear=-1.

2️⃣Нулевое значение не удаляет ключ из b. В обычном словаре наличие такого дополнительного ключа повлияло бы на равенство.

3️⃣С Python 3.10 сравнения Counter считают отсутствующие элементы нулевыми. В a счётчик pear равен нулю для сравнения, как и в b. Поэтому a == b даёт True.

4️⃣В c значение pear равно -1. Ноль и минус один различаются, поэтому a == c даёт False. Итоговая строка: True False.

Почему это важно
Равенство Counter проверяет частоты, а не одинаковый набор сохранённых ключей. Если в тесте важна структура записей, сравнивайте dict(a) и dict(b): здесь они различаются.
Please open Telegram to view this post
VIEW IN TELEGRAM
✍1
Please open Telegram to view this post
VIEW IN TELEGRAM
Два вызова после замены

1️⃣b.name создаёт связанный метод. Он хранит экземпляр b и функцию из класса Bot на момент обращения.

2️⃣tag сохраняет этот метод. Присваивание Bot.name = name заменяет атрибут класса, а сохранённую функцию внутри tag не меняет.

3️⃣Поэтому tag() вызывает прежнюю функцию и возвращает v1.

4️⃣b.name() ищет атрибут заново, уже после замены. Находит новую функцию и возвращает v2. print выводит v1 v2.

Почему это важно
Если сохранить метод как callback, последующая замена метода класса не обновит этот callback. При необходимости использовать текущую реализацию обращайтесь к методу непосредственно перед вызовом.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Какая точка исчезает

1️⃣PurePosixPath разбирает путь без обращения к файловой системе.

2️⃣Отдельный сегмент «.» удаляется при разборе. Поэтому a/./b превращается в a/b.

3️⃣Сегмент «..» остаётся. Если a окажется симлинком, сокращение a/../b до b может изменить смысл пути. PurePosixPath этого не проверяет.

4️⃣Поэтому первый print выводит a/b, а второй — a/../b. Программа не проверяет, существуют ли эти пути.

Почему это важно
Чистый объект пути не доказывает, что разные записи ведут к одному файлу. Для разрешения реального пути есть Path(...).resolve(), который обращается к файловой системе; у PurePosixPath такого метода нет.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Победитель при ничьей

1️⃣В votes числа 2 и 1 встречаются по два раза. По частоте они равны.

2️⃣multimode возвращает всех лидеров в порядке первого появления. Число 2 появилось раньше, поэтому первый print выводит [2, 1].

3️⃣В Python 3.8 и новее mode при ничьей выбирает первого встретившегося лидера. Это 2, поэтому второй print выводит 2.

4️⃣Числовой порядок здесь не используется. До Python 3.8 mode при такой ничьей выбрасывал StatisticsError — поэтому в условии указана версия.

Почему это важно
mode может вернуть одного лидера даже при ничьей. Если результат голосования должен явно показывать ничью, используйте multimode и проверяйте, сколько лидеров вернулось.
Please open Telegram to view this post
VIEW IN TELEGRAM