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

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

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

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

Другие наши проекты: https://tprg.ru/media
Download 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
Please open Telegram to view this post
VIEW IN TELEGRAM
Незаметный разделитель

1️⃣В text два разных управляющих символа: \n после alpha и \x0b между beta и gamma. Второй — вертикальная табуляция.

2️⃣split("\n") ищет только первый символ. beta и gamma остаются в одном элементе с табуляцией внутри. Получаются две части.

3️⃣splitlines распознаёт оба символа как границы строк. Получаются отдельные alpha, beta и gamma — три части.

4️⃣print выводит 2, затем 3. Помимо этих символов splitlines понимает, например, \r, пару \r\n и разделитель строк U+2028.

Почему это важно
При разборе текста способ разделения определяет границы записей. Проверяйте, какие управляющие символы возможны во входе: splitlines и split("\n") могут разбить один текст по-разному.
Please open Telegram to view this post
VIEW IN TELEGRAM
✍1
Please open Telegram to view this post
VIEW IN TELEGRAM
✍1
Почему копия среза не сработала

1️⃣cut — обратный срез с пропущенными границами, аналог [::-1]. Первый print выводит edcba.

2️⃣cut.indices(5) возвращает (4, -1, -1). Поэтому a=4, b=-1 и k=-1.

3️⃣Эти числа подходят для range: range(4, -1, -1) перечисляет индексы 4, 3, 2, 1, 0.

4️⃣Но в новом срезе s[4:-1:-1] значение -1 снова трактуется как индекс последнего символа, то есть 4. Старт и стоп совпадают: срез пуст. Второй print выводит пустую строку.

Почему это важно
Для применения исходного среза используйте s[cut]. Для обхода его индексов — range(*cut.indices(len(s))). Нормализованные границы нельзя механически превратить обратно в равнозначный срез.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Куда попала целая строка

1️⃣В name лежит cache. Двоеточия в этой строке нет.

2️⃣partition обычно возвращает тройку: часть до разделителя, сам разделитель, часть после него. Если разделитель не найден, результат — исходная строка и две пустые: ('cache', '', '').

3️⃣rpartition тоже возвращает три части, но ищет разделитель справа. При отсутствии разделителя он возвращает две пустые строки и исходную строку: ('', '', 'cache').

4️⃣Оба метода завершаются без ошибки. Различие видно по позиции cache: у первого метода она первая, у второго — последняя.

Почему это важно
При распаковке head, sep, tail проверяйте sep. Пустой sep означает, что разделитель не найден; исходная строка при этом может оказаться в head или tail, в зависимости от выбранного метода.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Почему timeout равен 30

1. В DEFAULT записано timeout="30". Секция api существует, но своих параметров у неё нет.

2. При чтении из api доступны значения DEFAULT. Поэтому первый getint берёт timeout из DEFAULT и преобразует строку "30" в целое число 30.

3. fallback используется, если параметр не найден. Явно переданное fallback=5 не переопределяет значение из DEFAULT.

4. Параметра retries нет ни в api, ни в DEFAULT. Второй getint возвращает 5. Код печатает две строки:
30
5

Если нужно изменить унаследованный timeout для api, задайте его в самой секции. fallback задаёт значение на случай отсутствия параметра. В этом примере используется стандартный ConfigParser без пользовательских преобразователей.

Документация Python
Please open Telegram to view this post
VIEW IN TELEGRAM
Какой аргумент выбирает обработчик

1. Декоратор singledispatch превращает route в функцию с несколькими реализациями. Исходная реализация зарегистрирована для object и возвращает "base". Она используется, если для типа первого аргумента не найден более подходящий обработчик.

2.
@route.register(int) добавляет обработчик для int, который возвращает "int".

3. В route("7", 7) первый аргумент — строка. Обработчик для str не зарегистрирован, поэтому вызывается исходная реализация и возвращает "base".

4. В route(7, "7") первый аргумент имеет тип int. Здесь выбирается зарегистрированная реализация, которая возвращает "int". Поэтому print выводит base int.

У обычной функции с singledispatch выбор зависит только от типа первого аргумента. Второй аргумент передаётся выбранному обработчику, но в выборе не участвует. Имя hint здесь — обычное имя параметра.

Документация Python
Please open Telegram to view this post
VIEW IN TELEGRAM
Куда делся первый status

1. DictReader берёт имена полей из первой строки: id, status, status. Следующая строка содержит три строковых значения: "7", "new", "done".

2. В CPython словарь для строки создаётся через dict(zip(fieldnames, row)). Ключ status встречается дважды. Второе присваивание сохраняет "done" поверх "new".

3. Поэтому вывод — done 2. В словаре два разных ключа, id и status. Ответ new 2 получился бы при сохранении первого значения, а done 3 — при подсчёте столбцов вместо ключей. ValueError при таком повторе не возникает.

При импорте чужого CSV проверяйте уникальность заголовков до преобразования в словарь. Если повторяющиеся столбцы нужны, читайте строки через csv.reader и сохраняйте их позиции. Отдельная ловушка: при короткой строке недостающее значение последнего status может заменить предыдущее на None.

Документация Python
✍1
Please open Telegram to view this post
VIEW IN TELEGRAM
Два пути к значению по умолчанию

1. Пустой список в parse_args([]) означает, что ни --limit, ни --rate не переданы. Аргументы запуска самого скрипта здесь не читаются.

2. Значение default="08" — строка. argparse пропускает её через type=int и получает число 8. Первый print выводит 8 int, без начального нуля.

3. Значение default=8.5 уже имеет тип float. Для нестрокового default преобразователь не вызывается. Вторая строка — 8.5 float.

Правильный вариант: 8 int | 8.5 float. Вариант со str пропускает преобразование строки. Два int получились бы при преобразовании обоих defaults, которого здесь нет. ValueError тоже не возникает: строка "08" допустима для int, а float вообще не передают конвертеру.

Если ожидается int, задавайте числовой default нужного типа. type=int проверяет переданные CLI-значения и строковые defaults, но не гарантирует тип любого программно заданного значения по умолчанию.

Документация Python
Please open Telegram to view this post
VIEW IN TELEGRAM
Что остаётся доступным после CycleError

1. В словаре TopologicalSorter значения — предшественники узла. Для build нужен test, а для test нужен build. Ни один из них не готов к запуску.

2. У lint нет предшественников. prepare() обнаруживает цикл и выбрасывает CycleError, который код перехватывает.

3. После такой ошибки разрешено вызвать get_ready(). Независимый lint остаётся доступным, поэтому вывод — ('lint',). Единственная готовая вершина делает порядок однозначным.

Пустой кортеж означал бы, что готовых узлов нет, хотя lint готов к запуску. Кортеж из всех трёх узлов нарушил бы зависимости. Повторного CycleError в get_ready() здесь нет: проверка цикла уже произошла в prepare().

Это позволяет выполнить независимую часть сборки или конвейера, пока циклическая часть заблокирована. Перехват ошибки не исправляет граф. Когда lint будет отмечен через done(), build и test всё равно не станут готовы.

Документация Python
Please open Telegram to view this post
VIEW IN TELEGRAM