Функции и аргументы: вопросы для собеседования (часть 2)
Позиционные и именованные аргументы, *args, **kwargs, значения по умолчанию, замыкания, lambda — всё это спрашивают на собеседованиях. Частая ловушка — мутабельный объект как значение по умолчанию. Умение декомпозировать код на функции и правильно работать с аргументами — признак зрелого разработчика.
Вопросы 6–10 из 20
6Есть функция `def profile(name, country='RU', city='Moscow'): return f'{name}:{country}:{city}'`. Что вернёт вызов `profile('Ann', city='Kazan')`?
AСтрока `'Ann:Kazan:RU'` при перезаписи параметра `country` именованным `city='Kazan'`
BСтрока `'Ann:RU:Kazan'` при позиционном `name`, дефолтном `country` и именованном `city`
CСтрока `'Ann:RU:Moscow'` при игнорировании `city='Kazan'` из-за дефолта `'Moscow'`
DОшибка `TypeError`: смешение позиционного и именованного аргумента в `profile()`
Ответ: Именованный аргумент позволяет переопределить конкретный параметр, не меняя остальные значения по умолчанию.
При вызове `profile('Ann', city='Kazan')` `name` передаётся позиционно, а `city` — по имени, поэтому `country` остаётся со значением по умолчанию `'RU'`. Итоговая f-строка соберётся как `'Ann:RU:Kazan'`. Именованные аргументы не перезаписывают порядок параметров и не игнорируются — наоборот, они позволяют переопределить нужный параметр, не трогая остальные дефолты. Сочетать позиционные и именованные аргументы можно, главное — не передавать одно и то же значение дважды.
Подробный разбор → 7Дан код: `x = 10` на верхнем уровне модуля и `def inc(): return x + 1`. Что вернёт вызов `inc()`?
A`NameError`: имя `x` не определено внутри `inc` и не видно из глобальной области
B`UnboundLocalError`: интерпретатор считает `x` локальной и обращается до присваивания
CЧисло `10`: функция возвращает само глобальное значение `x` без выполнения сложения
DЧисло `11`: имя `x` читается из глобальной области по правилу LEGB и прибавляется `1`
Ответ: Если переменная только читается, Python берёт её из глобальной области (правило LEGB).
По правилу областей видимости (LEGB) Python ищет имя сначала локально, затем во внешних, глобальной и встроенной областях. В `inc()` `x` не присваивается, а только читается, поэтому используется глобальное `x = 10`, и результат `x + 1` равен 11. `UnboundLocalError` появляется только если внутри функции есть присваивание `x = ...` — тогда `x` считается локальной, но к моменту чтения ещё не определена.
Подробный разбор → 8Дана функция `def f(a, b, c): return a * 100 + b * 10 + c`. Что вернёт вызов `f(1, c=3, b=2)`?
AЧисло `132` при подстановке `1`, `2`, `3` справа налево по позициям сигнатуры
BЧисло `123` при подстановке `1` в `a`, а именованных `b=2` и `c=3` по именам
C`TypeError` при попытке смешать `1` позиционно и `b=2` с `c=3` по имени
DЧисло `321` при сборке цифр в обратном порядке параметров `a`, `b`, `c`
Ответ: Позиционные аргументы заполняют параметры слева направо, именованные можно указывать в любом порядке по имени.
В вызове `f(1, c=3, b=2)` значение 1 идёт в параметр `a` позиционно. Параметры `b` и `c` задаются именованно, поэтому их порядок не важен. В итоге считается `1 * 100 + 2 * 10 + 3`, то есть 123. Смешивать позиционные и именованные аргументы можно — главное, чтобы позиционные шли первыми и не дублировались с именованными.
Подробный разбор → 9Есть функция `def f(a, b): return a + b`. Что произойдёт при вызове `f(1, a=2)`?
AЧисло 3 после передачи 1 в `a` позиционно и `a=2` в `b` по остатку имён
BЧисло 2 после перезаписи позиционного `a` именованным `a=2` и `b=0` дефолтом
CЧисло 1 после игнорирования именованного `a=2` и сохранения позиционного значения
DОшибка `TypeError`: параметр `a` получил значение дважды позиционно и по имени
Ответ: Нельзя передать один и тот же параметр одновременно позиционно и по имени — это вызовет ошибку.
Вызов `f(1, a=2)` передаёт значение для параметра `a` дважды: сначала позиционно (`1`), затем именованно (`a=2`). Python не может выбрать «какое правильное» и выбрасывает ошибку вида `TypeError: got multiple values for argument`. Это частая ошибка при смешанном стиле вызовов. Тихого приоритета или игнорирования здесь нет.
Подробный разбор → 10Дана функция `def split_total(total): return total // 100, total % 100`. Что вернёт вызов `split_total(250)`?
A`(2, 50)`: кортеж из частного `total // 100` и остатка `total % 100` от деления
B`(250, 0)`: кортеж из исходного числа и нулевого остатка от деления на сотню
C`[2, 50]`: список из двух чисел, поскольку запятая в `return` создаёт `list`
D`TypeError`: возврат нескольких значений требует явных скобок кортежа в `return`
Ответ: Функция может вернуть несколько значений — фактически это один `tuple`.
Выражение после `return` содержит два значения через запятую — Python автоматически собирает их в кортеж `tuple`. Для 250: `250 // 100` равно 2, а `250 % 100` равно 50. Итог — `(2, 50)`. Такой стиль часто используют для возврата связанных результатов, которые затем удобно распаковать: `a, b = split_total(250)`. Список через запятую без скобок Python не создаёт, и ошибки тут тоже не возникает.
Подробный разбор →