Функции и аргументы: вопросы для собеседования (часть 4)

Позиционные и именованные аргументы, *args, **kwargs, значения по умолчанию, замыкания, lambda — всё это спрашивают на собеседованиях. Частая ловушка — мутабельный объект как значение по умолчанию. Умение декомпозировать код на функции и правильно работать с аргументами — признак зрелого разработчика.

Коллекции и структуры данныхГенераторы списков и встроенные функцииЦиклы и условияИсключения и отладкаРабота с файлами: JSON и CSVИтераторы и генераторыNumPy: основыPandas и DataFrameСинтаксис и типы данных

Вопросы 1620 из 20

16Какой вариант корректно избегает ловушки с изменяемым значением по умолчанию для списка?
A`def add(x, items=[]): items.append(x); return items`
B`def add(x, items=None): items = [] if items is None else items; items.append(x); return items`
C`def add(x, items=()): items.append(x); return items`
D`def add(x, items={}): items.append(x); return items`
Ответ: Безопасный паттерн: ставить дефолт `None` и создавать новый список внутри функции.

Изменяемые дефолты (список, словарь, множество) создаются один раз и переиспользуются. Чтобы избежать общих побочных эффектов между вызовами, обычно ставят `items=None`, а внутри делают инициализацию: `items = [] if items is None else items`. Тогда при каждом вызове без `items` создаётся новый список, и данные не «накапливаются» неожиданно.

17Вы пишете обертку над функцией `render(template, **options)`, которая принимает много опций. Вам нужно пробрасывать дополнительные опции дальше, но при этом оставить обязательный параметр явным. Какой вариант лучше?
A`def render_page(template, options): return render(template, options)`
B`def render_page(template, *options): return render(template, *options)`
C`def render_page(*args, **kwargs): return render(*args, **kwargs)`
D`def render_page(template, **options): return render(template, **options)`
Ответ: В обертках часто делают обязательные параметры явными, а остальное пробрасывают через `**kwargs`.

Вариант `def render_page(template, **options): return render(template, **options)` сохраняет читаемость: сразу видно обязательный `template`. При этом `**options` позволяет пробросить любые дополнительные именованные параметры дальше. Полностью «универсальный» `*args, **kwargs` иногда нужен, но ухудшает понимание интерфейса, если у функции есть очевидные обязательные аргументы.

18Вы проектируете функцию для аналитического трекинга: обязательны `event_name`, `user_id`, `timestamp`, а дополнительные поля бывают редко и непредсказуемы. Какой дизайн наиболее читаемый и удобный?
A`def track_event(**kwargs): ...`
B`def track_event(event_name, *args, **kwargs): ...`
C`def track_event(): ...`
D`def track_event(event_name, user_id, timestamp, **kwargs): ...`
Ответ: Ключевые обязательные параметры лучше делать явными, а «опциональные метаданные» — через `**kwargs`.

Если обязательные параметры важны для понимания кода, их стоит прописать явно в сигнатуре: так легче читать, есть автодополнение, меньше ошибок при вызове. При этом дополнительные редкие поля удобно принимать через `**kwargs` как словарь метаданных. Полностью «все через `**kwargs`» ухудшает читаемость и усложняет контроль входных данных.

19Дана функция `def add_item(item, items=[]): items.append(item); return items`. Что вернет второй вызов, если выполнить `add_item('a')`, а затем `add_item('b')`?
A`['b']`
B`['a']`
C`['a', 'b']`
D`[]`
Ответ: Изменяемое значение по умолчанию создаётся один раз и затем переиспользуется между вызовами.

Значение по умолчанию для `items` (пустой список) создается в момент определения функции, а не при каждом вызове. Поэтому `add_item('a')` добавляет `'a'` в тот же список, который затем используется и во втором вызове. Второй вызов `add_item('b')` добавит `'b'` в этот же список и вернет `['a', 'b']`. Это частая ловушка со значениями по умолчанию.

20Вы пишете функцию среднего значения, и в коде чаще всего уже есть список `values` (например, из данных). Какой интерфейс обычно более универсален и читаем?
A`def mean(*values): ...`
B`def mean(**values): ...`
C`def mean(values): ...`
D`def mean(): ...`
Ответ: Если вход чаще уже «как коллекция», лучше принимать один параметр-итерируемый, чем заставлять распаковывать в `*args`.

Сигнатура `def mean(values): ...` позволяет передавать список, кортеж или любой другой итерируемый объект напрямую (например, `mean(values)`), без распаковки. Вариант `def mean(*values): ...` удобен, когда вызывают как `mean(1, 2, 3)`, но хуже подходит для данных, которые уже собраны в коллекции. При проектировании функций для аналитики чаще выигрывает явный параметр `values`.

1234

Хотите тренировать интерактивно?

В приложении — таймер, прогресс, стрики и 1700+ вопросов по всем темам.

Тренировать в Telegram

Другие темы: Python

Коллекции и структуры данныхГенераторы списков и встроенные функцииЦиклы и условияИсключения и отладкаРабота с файлами: JSON и CSVИтераторы и генераторыNumPy: основыPandas и DataFrameСинтаксис и типы данных