Аргументы функций в Python: *args, **kwargs, значения по умолчанию
Этот урок переведён с английского оригинала.Read in EnglishLeer en españolСообщить об ошибке в переводе
Вспомните, как вы заказываете кофе. Размер выбрать нужно всегда, это обязательно. А добавки вроде овсяного молока, дополнительной порции эспрессо или карамельного сиропа? Они по желанию. Вы называете их, только когда они вам нужны.
Аргументы функций в Python устроены так же. Одни обязательны, у других есть разумные значения по умолчанию, а третьи позволяют передать сколько угодно дополнительных данных. Именно владение этими приёмами отличает новичка от того, кто пишет по-настоящему гибкий код.
В этом уроке вы узнаете все способы, которыми Python позволяет передавать данные в функции: от простых позиционных аргументов до мощных приёмов *args и **kwargs, которые используются в настоящих библиотеках.
Позиционные и именованные аргументы
Когда вы вызываете функцию, Python сопоставляет аргументы с параметрами двумя способами. Позиционные аргументы сопоставляются по их позиции: первый аргумент попадает в первый параметр. Именованные аргументы сопоставляются по имени.
Whiskers is a cat Buddy is a dog
Именованный аргумент записывается как parameter_name=value. Такой вызов сам себя документирует: любой, кто читает код, точно видит, что означает каждое значение, даже не заглядывая в определение функции.
В одном вызове можно сочетать оба стиля, но позиционные аргументы должны идти перед именованными.
Whiskers is a 3-year-old cat
Как значения по умолчанию делают функции гибкими
Значения по умолчанию позволяют объявлять необязательные параметры. Если вызывающий код передал значение, используется оно. Если нет, в дело вступает значение по умолчанию.
large coffee medium coffee with oat milk small coffee with almond milk, 2 sugar(s)
У параметра size нет значения по умолчанию, поэтому он обязателен. У параметров milk и sugar значения по умолчанию есть, так что они необязательны. Именно так устроены многие настоящие библиотеки Python: несколько обязательных параметров и много необязательных с разумными значениями по умолчанию.
Ловушка изменяемого значения по умолчанию
Это один из самых известных подвохов Python. Если в качестве значения по умолчанию использовать изменяемый объект (например, список), этот объект создаётся один раз при определении функции, а не при каждом её вызове.
Это значит, что все вызовы работают с одним и тем же списком. Изменения из одного вызова переходят в следующий. Посмотрим, как это выглядит:
['apples'] ['apples', 'bread'] ['apples', 'bread', 'milk']
Удивлены? Каждый вызов должен был начинаться с нового списка, а список всё растёт. Так происходит потому, что [] создаётся один раз, когда функция определяется, и затем используется при каждом вызове.
Исправить это помогает известный в Python приём: используйте None как значение по умолчанию и создавайте новый список внутри функции.
['apples'] ['bread'] ['milk']
Что делает *args?
Иногда заранее неизвестно, сколько аргументов получит функция. Например, вам нужна функция, которая складывает любое количество чисел: 2, 5 или 100.
Синтаксис *args собирает все лишние позиционные аргументы в кортеж. Затем их можно перебрать в цикле, обратиться к ним по индексу или сделать с ними всё, что делают с кортежем.
6 100 5
Имя args всего лишь соглашение. Можно написать *numbers, *items или *values. Вся магия в *: звёздочка говорит Python упаковать все оставшиеся позиционные аргументы в кортеж.
<class 'tuple'>
('hello', 42, True)
Argument 0: hello
Argument 1: 42
Argument 2: True*args можно сочетать с обычными параметрами. Сначала свои значения забирают обычные параметры, а затем *args собирает всё, что осталось.
Hello, Alice! Hello, Bob! Hello, Charlie!
Что делает **kwargs?
Если *args собирает лишние позиционные аргументы, то **kwargs собирает лишние именованные аргументы в словарь. Ключами становятся имена аргументов, а значениями их значения.
name: Alice age: 25 city: Paris
Как и args, имя kwargs всего лишь соглашение. Можно написать **info, **options или **settings. Главное здесь **: две звёздочки говорят Python упаковать лишние именованные аргументы в словарь.
Это невероятно удобно для функций, которые должны быть гибкими. Вы можете передавать любые именованные данные, не меняя определение функции.
Username: alice_99 email: alice@mail.com age: 25 Username: bob_42 city: London
Все виды аргументов вместе
В одной функции можно использовать обычные параметры, значения по умолчанию, *args и **kwargs. Но порядок важен. Python требует вот такой последовательности:
*args**kwargsOrder from: Pizza Palace Items: margherita, garlic bread Delivery: True tip: 5 note: extra cheese
def process(*args, **kwargs):
# What does this expect?
passdef process(data, format='json',
verbose=False):
# Easy to understand
passУпражнения для практики
Что выведет этот код? Введите точный вывод.
def power(base, exponent=2):
print(base ** exponent)
power(5)
power(2, 10)
power(exponent=3, base=4)Напишите функцию greet, которая принимает параметр name и необязательный параметр greeting со значением по умолчанию 'Hello'. Она должна выводить <greeting>, <name>!. Например, greet('Alice') выводит Hello, Alice!, а greet('Bob', 'Hey') выводит Hey, Bob!.
Напишите функцию sum_all, которая с помощью *args принимает любое количество числовых аргументов и возвращает их сумму. Если аргументы не переданы, верните 0.
Эта функция должна каждый раз создавать новый список, но в ней есть ошибка с изменяемым значением по умолчанию. Исправьте её так, чтобы каждый вызов начинался с нового списка.
Ожидаемый вывод:
[1]
[2]
[3]Напишите функцию build_profile, которая принимает обязательный параметр name и с помощью **kwargs принимает любые дополнительные сведения. Она должна вывести имя в первой строке, а затем каждую пару «ключ: значение» на отдельной строке в формате key: value (с двумя пробелами в начале). Выводите ключи в том порядке, в котором они переданы.
Пример: build_profile('Alice', age=25, city='NYC') должна вывести:
Alice
age: 25
city: NYCНапишите функцию calc, которая принимает строку operation ('add', 'multiply' или 'max'), а затем с помощью *args любое количество числовых аргументов. Функция должна возвращать результат:
'add': сумма всех чисел (0, если чисел нет)'multiply': произведение всех чисел (1, если чисел нет)'max': наибольшее число (0, если чисел нет)Примеры: calc('add', 1, 2, 3) возвращает 6. calc('multiply', 2, 3, 4) возвращает 24.
Перепишите функцию ниже так, чтобы вместо отдельных параметров она использовала **kwargs. Функция должна принимать name как обязательный параметр, а всё остальное через **kwargs. Выведите имя, а затем каждое сведение на отдельной строке в формате key: value. Вывод должен совпадать в точности.
Исходная версия:
def show_student(name, grade, school, gpa):
print(name)
print('grade: ' + str(grade))
print('school: ' + school)
print('gpa: ' + str(gpa))Ожидаемый вывод для show_student('Alice', grade=10, school='Lincoln', gpa=3.8):
Alice
grade: 10
school: Lincoln
gpa: 3.8