Циклы и условия: вопросы для собеседования (часть 4)
for, while, if/elif/else, break, continue — управление потоком выполнения программы. Задачи на циклы проверяют умение итерироваться по сложным структурам, обрабатывать граничные случаи и писать читаемую логику. Часто дают задачу, где нужно обойти вложенные данные или реализовать несложный алгоритм.
Вопросы 16–20 из 20
16Поле `country` может быть `None` или пустой строкой. Нужно заменить его на `'unknown'` через `if`. Какое условие покрывает оба случая короче и идиоматичнее?
A`if country is None: country = 'unknown'`
B`if not country: country = 'unknown'`
C`if country == '': country = 'unknown'`
D`if country == None or country == '': country = 'unknown'`
Ответ: `not country` истинно для `None`, пустой строки и других «пустых» значений в Python.
Проверка `if not country:` срабатывает и для `None`, и для `''`, и для других falsy-значений (`0`, `[]`). Сравнение `country is None` пропустит пустую строку. Условие `if country:` сработает наоборот — заменит непустые значения. Длинная связка `country == None or country == ''` тоже работает, но менее идиоматична: в Python с `None` принято сравнивать через `is`, а не `==`, и нет смысла писать два условия там, где достаточно `not country`.
Подробный разбор → 17В каком случае обычно уместнее выбрать цикл `while`, а не `for`?
AКогда нужно последовательно пройти по элементам известного списка `users` и обработать каждого
BКогда нужно параллельно пройти по двум спискам сразу, используя обычно `zip()` внутри тела цикла
CКогда нужно пропустить текущую итерацию по условию через инструкцию `continue` внутри тела цикла
DКогда количество итераций заранее неизвестно и цикл завершается по условию, обновляемому в теле
Ответ: `for` удобен для перебора последовательностей, а `while` — для повторения «пока условие истинно» с заранее неизвестным числом итераций.
Если вы перебираете известную коллекцию (список, словарь, строку), обычно выбирают `for`. Если же цикл должен завершаться по условию, которое меняется внутри тела (например, «пока не встретили признак конца» или «пока счётчик не достиг порога»), то логичнее `while`. Параллельный обход двух списков и пропуск итерации через `continue` — отдельные приёмы, не относящиеся к выбору между `for` и `while`.
Подробный разбор → 18Нужно задать значение скидки без лишней вложенности: если `country == 'RU'` и `is_premium` истина → 10%; если `country == 'RU'` и не premium → 0; иначе → 5%. Какой вариант логики `if`/`elif`/`else` корректен?
A`if country == 'RU' and is_premium:` → 10%; `elif country == 'RU':` → 0; `else:` → 5%
B`if country == 'RU':` → 0; `elif country == 'RU' and is_premium:` → 10%; `else:` → 5%
C`if country != 'RU':` → 5%; `elif is_premium:` → 0; `else:` → 10%
D`if country == 'RU' or is_premium:` → 10%; `elif country == 'RU':` → 0; `else:` → 5%
Ответ: Сначала ставьте более специфичное условие (`country` и `is_premium` вместе), затем более общее (`country == 'RU'`).
Когда условия пересекаются, более специфичное должно идти раньше. Если поставить `country == 'RU'` первым, премиальные пользователи из России попадут в эту ветку и до проверки `is_premium` дело не дойдёт. Поэтому начинаем с комбинации `country == 'RU' and is_premium`, затем обрабатываем оставшийся случай `country == 'RU'`, и в конце общий `else`. Вариант с `or` объединяет несвязанные случаи, а перестановка ломает логику.
Подробный разбор → 19Нужно найти индекс первого отрицательного значения в списке `amounts = [10, 0, -5, 7]`. Выполняется `idx = None`, затем цикл `for i, a in enumerate(amounts):`, и при `if a < 0:` выполняются `idx = i` и `break`. Чему будет равно `idx` после выполнения?
A`None`: исходное значение сохраняется, как если бы цикл не выполнил `break` ни разу
B`0`: индекс самого первого элемента, при котором условие сработало бы сразу же
C`2`: индекс элемента `-5`, на котором условие истинно и срабатывает `break`
D`3`: индекс последнего элемента, как если бы цикл прошёл до конца без выхода
Ответ: Функция `enumerate()` даёт индекс и значение, а `break` останавливает цикл на первом совпадении.
Пары из `enumerate(amounts)` идут так: (0, 10), (1, 0), (2, -5), (3, 7). На значении -5 условие `a < 0` впервые истинно, поэтому `idx` становится 2 и сразу срабатывает `break` — цикл прерывается. Значение `None` осталось бы только если ни одно условие не сработало. Индекс 0 был бы при отрицательном первом элементе, а 3 — если бы условие сработало на последнем.
Подробный разбор → 20Что окажется в `out` после кода: `out = []`, внешний цикл `for u in ['u1', 'u2']`, внутренний `for e in ['a', 'b', 'c']`, при `e == 'b'` выполняется `break`, иначе `out.append(u + e)`?
A`['u1a', 'u1b', 'u2a', 'u2b']`: оба внутренних элемента до `'b'` добавлены без срабатывания `break`
B`['u1a', 'u2a']`: внутренний цикл прерывается на `'b'`, внешний продолжает обход следующим `u`
C`['u1a', 'u2a', 'u2b']`: инструкция `break` срабатывает один раз и не влияет на следующий `u`
D`['u1a']`: инструкция `break` завершает оба цикла одновременно и больше элементов не добавляется
Ответ: `break` завершает только ближайший внутренний цикл, внешний цикл продолжает обход.
Для `u = 'u1'` сначала добавится `'u1a'`, затем при `e == 'b'` сработает `break` и внутренний цикл остановится. Внешний цикл перейдёт к `u = 'u2'` и аналогично добавит `'u2a'`. Итог — `['u1a', 'u2a']`. Чтобы выйти сразу из обоих циклов, нужен дополнительный флаг или вынос кода в функцию с `return`, потому что один `break` всегда прерывает только свой уровень вложенности.
Подробный разбор →