Python: задачки и вопросы
Развёрнутое пояснение:
1. При определении функции ingest создаётся один объект множества set() и сохраняется как значение по умолчанию для параметра seen.
2. Первый вызов ingest(['a','b']) использует это же множество, добавляет в него 'a' и 'b' и возвращает len(seen), то есть 2.
3. Второй вызов ingest(['b','c']) получает уже непустое множество с элементами 'a' и 'b'. Добавляется только 'c', поэтому длина становится 3.
4. Переменная res получает кортеж (2, 3), а print(*res) выводит 2 3.
5. Правильный вариант — 2 3.
Почему это важно: изменяемое значение по умолчанию — частая ловушка при реализации кэшей, дедупликаторов, счётчиков и накопителей ошибок. В тесте, где функцию вызывают один раз, баг может не проявиться, а в проде при повторных вызовах общее изменяемое состояние искажает результат. Чтобы избежать этого, изменяемый объект создают внутри тела функции: seen=None, а внутри if seen is None: seen=set().
2. Первый вызов ingest(['a','b']) использует это же множество, добавляет в него 'a' и 'b' и возвращает len(seen), то есть 2.
3. Второй вызов ingest(['b','c']) получает уже непустое множество с элементами 'a' и 'b'. Добавляется только 'c', поэтому длина становится 3.
4. Переменная res получает кортеж (2, 3), а print(*res) выводит 2 3.
5. Правильный вариант — 2 3.
Почему это важно: изменяемое значение по умолчанию — частая ловушка при реализации кэшей, дедупликаторов, счётчиков и накопителей ошибок. В тесте, где функцию вызывают один раз, баг может не проявиться, а в проде при повторных вызовах общее изменяемое состояние искажает результат. Чтобы избежать этого, изменяемый объект создают внутри тела функции: seen=None, а внутри if seen is None: seen=set().
✍1
Python: задачки и вопросы
Развёрнутое пояснение:
1. from fractions import Fraction импортирует рациональное число.
2. Fraction(0.1) берёт не 1/10, а точное значение float 0.1 — дробь с большим знаменателем.
3. То же происходит с Fraction(0.2) и Fraction(0.3).
4. a + b вычисляет сумму двух таких двоичных дробей.
5. Эта сумма не равна дроби, полученной из float 0.3, потому что 0.1 + 0.2 ≠ 0.3 в двоичной плавающей арифметике.
6. Сравнение возвращает False.
Почему это важно: при финансовых расчётах и точных вычислениях Fraction следует создавать из строк или целых чисел, а не из float, иначе двоичные погрешности сохраняются в «точном» рациональном типе.
2. Fraction(0.1) берёт не 1/10, а точное значение float 0.1 — дробь с большим знаменателем.
3. То же происходит с Fraction(0.2) и Fraction(0.3).
4. a + b вычисляет сумму двух таких двоичных дробей.
5. Эта сумма не равна дроби, полученной из float 0.3, потому что 0.1 + 0.2 ≠ 0.3 в двоичной плавающей арифметике.
6. Сравнение возвращает False.
Почему это важно: при финансовых расчётах и точных вычислениях Fraction следует создавать из строк или целых чисел, а не из float, иначе двоичные погрешности сохраняются в «точном» рациональном типе.
✍4
Как понять, что программисту пора в отпуск:
— на столе бардак;
— шорты не доставались с позапрошлого лета;
— чудится тифлинг;
— на вопрос «когда отдыхаешь?» отвечает «после релиза»;
— релиз был в феврале.
Сам он с места не сдвинется. Помогите Типичному Программисту собраться и улететь в отпуск в новой мини-игре!
— на столе бардак;
— шорты не доставались с позапрошлого лета;
— чудится тифлинг;
— на вопрос «когда отдыхаешь?» отвечает «после релиза»;
— релиз был в феврале.
Сам он с места не сдвинется. Помогите Типичному Программисту собраться и улететь в отпуск в новой мини-игре!
Python: задачки и вопросы
Развёрнутое пояснение:
1. Внутри report создаётся объект map, который при обходе вычисляет len для каждой строки.
2. sum(sizes) проходит по всем элементам: 3 + 4 + 2 = 9. После этого итератор исчерпан.
3. list(sizes) пытается снова обойти тот же объект map, но элементов больше нет, поэтому valid = 0.
4. Функция возвращает кортеж (9, 0), который и печатается.
Почему это важно: в пайплайнах обработки логов или событий часто передают map, filter или генераторное выражение сразу в несколько агрегирующих функций, ожидая, что каждая из них прочитает данные заново. Из-за одноразовости итераторов это приводит к заниженным счётчикам, потере записей и нестабильным отчётам, пока данные не материализовать в список.
2. sum(sizes) проходит по всем элементам: 3 + 4 + 2 = 9. После этого итератор исчерпан.
3. list(sizes) пытается снова обойти тот же объект map, но элементов больше нет, поэтому valid = 0.
4. Функция возвращает кортеж (9, 0), который и печатается.
Почему это важно: в пайплайнах обработки логов или событий часто передают map, filter или генераторное выражение сразу в несколько агрегирующих функций, ожидая, что каждая из них прочитает данные заново. Из-за одноразовости итераторов это приводит к заниженным счётчикам, потере записей и нестабильным отчётам, пока данные не материализовать в список.
✍3
Python: задачки и вопросы
Развёрнутое пояснение:
1. При определении функции log_event создаётся один пустой список buffer, который используется как значение по умолчанию.
2. Первый вызов log_event со строкой timeout добавляет эту строку в общий список и возвращает его, результат сохраняется в переменную a.
3. Второй вызов log_event со строкой 403 использует тот же список buffer, добавляет строку 403 и возвращает его, результат сохраняется в переменную b.
4. Переменные a и b указывают на один и тот же объект, поэтому print(a, b) выводит ['timeout', '403'] ['timeout', '403'].
Почему это важно: изменяемый аргумент по умолчанию часто встречается в функциях-логгерах, кэширующих обёртках и накопителях метрик, но на деле он работает как глобальное состояние между вызовами. В многопоточном коде это создаёт состояние гонки, а в тестах — нестабильные результаты. Правильный паттерн: buffer=None и создание нового списка внутри функции.
2. Первый вызов log_event со строкой timeout добавляет эту строку в общий список и возвращает его, результат сохраняется в переменную a.
3. Второй вызов log_event со строкой 403 использует тот же список buffer, добавляет строку 403 и возвращает его, результат сохраняется в переменную b.
4. Переменные a и b указывают на один и тот же объект, поэтому print(a, b) выводит ['timeout', '403'] ['timeout', '403'].
Почему это важно: изменяемый аргумент по умолчанию часто встречается в функциях-логгерах, кэширующих обёртках и накопителях метрик, но на деле он работает как глобальное состояние между вызовами. В многопоточном коде это создаёт состояние гонки, а в тестах — нестабильные результаты. Правильный паттерн: buffer=None и создание нового списка внутри функции.
✍1