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

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

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

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

Другие наши проекты: https://tprg.ru/media
Download Telegram
Please open Telegram to view this post
VIEW IN TELEGRAM
3
Python: задачки и вопросы
Развёрнутое пояснение:

1. Создаётся словарь base с ключом retry, значением которого является другой словарь {limit: 3}.

2. copy.copy(base) создаёт новый внешний словарь cfg, но значение по ключу retry — это тот же вложенный словарь, а не его отдельная копия.

3. Через cfg['retry']['limit'] = 5 изменяется значение во вложенном словаре, на который ссылаются и base, и cfg.

4. Поэтому base['retry']['limit'] теперь равно 5, и print выводит 5.

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

1. Импортируется Counter из collections.

2. a получает значения x=2, y=1; b получает x=1, y=2.

3. Операция a - b вычитает счётчики по каждому ключу: x становится 2 - 1 = 1, y становится 1 - 2 = -1.

4. Counter автоматически отбрасывает ключи с неположительным значением, поэтому результат содержит только x=1.

5. Метод elements() возвращает итератор, который выдаёт каждый ключ столько раз, чему равен его положительный счётчик: здесь 'x' один раз.

6. list(...) превращает итератор в список ['x'], который и печатает print.

Почему это важно: при сравнении частот событий, тегов или ошибок за два периода часто используют вычитание Counter. Важно помнить, что отрицательные и нулевые значения исчезают из результата, а elements() не покажет отсутствующие ключи, иначе можно принять просевшие метрики за новые события.
2
Please open Telegram to view this post
VIEW IN TELEGRAM
2
Please open Telegram to view this post
VIEW IN TELEGRAM
1
Python: задачки и вопросы
Развёрнутое пояснение:

1. Цикл for event_type in ('click', 'scroll') начинает первую итерацию: event_type = 'click'.

2. На этой итерации определяется функция handler(e=event_type). Значение по умолчанию для e вычисляется сейчас и сохраняется как 'click' внутри объекта функции.

3. Переменная event_type в теле handler не является локальной: она свободная и будет искаться в объемлющей области видимости во время вызова.

4. Вторая итерация: event_type = 'scroll'. Определяется новая функция handler, её значение по умолчанию для e — 'scroll'.

5. После цикла event_type остаётся равной 'scroll', так как это последнее значение итерируемого.

6. Вызов handlers[0]() использует значение по умолчанию e='click' и берёт event_type='scroll' из замыкания, возвращая ('click', 'scroll').

7. Вызов handlers[1]() использует e='scroll' и event_type='scroll', возвращая ('scroll', 'scroll').

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

1. Вызов await t(1) запускает первую корутину: на печать идёт 1, затем await sleep(0) приостанавливает выполнение main и отдаёт управление event loop.

2. create_task(t(3)) только планирует фоновую задачу в очередь event loop, но не запускает её немедленно.

3. await t(2) печатает 2 и тоже приостанавливается на sleep(0).

4. Event loop видит три готовых продолжения: t(1), запланированная t(3) и main после t(2). Порядок FIFO, поэтому сначала возобновляется t(1), которая печатает 2 и завершается.

5. Следующей запускается t(3): печатается 3, затем await sleep(0) приостанавливает её.

6. Затем event loop возвращает управление в main после await t(2), где сразу выполняется await x и main ждёт завершения t(3).

7. Event loop возобновляет t(3), печатается 4, задача завершается.

8. В результате на печать идёт последовательность 122334.

Почему это важно: в асинхронном коде побочные эффекты и порядок записи строк не всегда совпадают с порядком выполнения. sleep(0) — не пустая операция, а точка уступки управления, после которой event loop выбирает следующую задачу из очереди. Это влияет на порядок логов, обновление кэша, коммитов состояния и других операций с побочными эффектами, поэтому важно понимать, что create_task планирует задачу, а await передаёт управление.
2
Please open Telegram to view this post
VIEW IN TELEGRAM
1
Python: задачки и вопросы
Развёрнутое пояснение:

1. В строке from decimal import Decimal импортируется тип Decimal.

2. Выражение Decimal('0.1') + Decimal('0.2') выполняет точное десятичное сложение и даёт Decimal('0.3').

3. Сравнение a == 0.3 сравнивает Decimal с float. Python преобразует float 0.3 в Decimal через его двоичное приближение, которое не равно Decimal('0.3'), поэтому результат False.

4. Сравнение a == Decimal('0.3') сравнивает два одинаковых Decimal, поэтому True.

5. float(a) превращает Decimal('0.3') в float 0.3, и сравнение с литералом 0.3 даёт True.

6. Итоговый вывод: False True True.

Почему это важно: В финансовых расчётах и работе с ценами часто используют Decimal, чтобы избежать ошибок float. Но при смешивании Decimal и float в сравнениях, сортировках или условиях результат может оказаться неожиданным: равенство может быть ложным, а max или min может выбрать не тот элемент. Правильно — приводить оба операнда к Decimal или к float явно и не полагаться на неявное преобразование.
3
Please open Telegram to view this post
VIEW IN TELEGRAM
2
Python: задачки и вопросы
Развёрнутое пояснение:

1. MRO класса D(B, C): D, B, C, A.

2. D().f() печатает D и через super().f() делегирует следующему классу в MRO — B.

3. B.f печатает B и через super().f() делегирует следующему — C, а не A, потому что super() опирается на линейный MRO объекта, а не на прямого родителя.

4. C.f печатает C и через super().f() делегирует A.

5. A.f печатает A.

6. Итоговый вывод: DBCA.

Почему это важно: понимание MRO и super() нужно при проектировании миксинов и множественного наследования, иначе можно случайно пропустить ветку или вызвать метод дважды.
4
Please open Telegram to view this post
VIEW IN TELEGRAM
1
Python: задачки и вопросы
Развёрнутое пояснение:

1. При первом обращении c.ports вызывается метод ports и возвращается список [80].

2. cached_property записывает этот объект в c.__dict__ под именем ports.

3. Вызов c.ports.append(443) изменяет именно тот список, который теперь лежит в кэше.

4. При повторном обращении c.ports cached_property находит ports в __dict__ и возвращает сохранённый объект [80, 443].

5. print выводит [80, 443].

Почему это важно: cached_property удобен для дорогих вычислений, но кэшированный объект остаётся общим для всех последующих обращений. Если метод возвращает изменяемую коллекцию и кто-то её мутирует, изменения застревают в кэше. В конфигах и синглтонах это приводит к «засорению» настроек между тестами или запросами: безопаснее возвращать неизменяемые копии или отделять изменяемое состояние от кэшированного свойства.
3
Please open Telegram to view this post
VIEW IN TELEGRAM
1
Python: задачки и вопросы
Развёрнутое пояснение:

1. State — перечисление, член State.OK имеет значение ok, но сам является отдельным объектом Enum.

2. Функция check сравнивает строку ok с объектом State.OK оператором равенства.

3. У Enum операция равенства считает равными только члены одного и того же перечисления, поэтому сравнение со строкой возвращает False.

4. В результате print выводит False.

Почему это важно: при рефакторинге строковых констант в Enum часто забывают, что сравнение со строкой из JSON или БД перестаёт работать. Правильно сравнивать через State(raw) == State.OK или raw == State.OK.value. Это типичная ошибка в валидации API и конфигурации.
3
Please open Telegram to view this post
VIEW IN TELEGRAM
2
Python: задачки и вопросы
Развёрнутое пояснение:

1. Создаётся пустой список queue.

2. defaultdict инициализируется фабрикой lambda: queue, которая при каждом обращении по отсутствующему ключу возвращает ссылку на этот же список.

3. tasks['api'] вызывает фабрику, возвращает queue и добавляет в него строку retry.

4. tasks['queue'] снова вызывает фабрику и возвращает тот же список queue, который уже содержит retry.

5. Поэтому print выводит ['retry'].

Почему это важно: такая ошибка возникает, когда разработчик хочет инициализировать значение изменяемым объектом и вместо конструктора list передаёт lambda, замкнутую на заранее созданный объект. В планировщиках задач, группировке событий по категориям или кэшировании результатов по ключам это приводит к тому, что разные ключи делят одно состояние. Правильный способ — передать list или функцию, создающую новый объект при каждом вызове, например defaultdict(list).
2