Перейти к содержимому
Editor

Аргументы функций в Python: *args, **kwargs, значения по умолчанию

Средний25 min7 упр.105 XP

Этот урок переведён с английского оригинала.Read in EnglishLeer en españolСообщить об ошибке в переводе

0/7 упр.

Вспомните, как вы заказываете кофе. Размер выбрать нужно всегда, это обязательно. А добавки вроде овсяного молока, дополнительной порции эспрессо или карамельного сиропа? Они по желанию. Вы называете их, только когда они вам нужны.

Аргументы функций в Python устроены так же. Одни обязательны, у других есть разумные значения по умолчанию, а третьи позволяют передать сколько угодно дополнительных данных. Именно владение этими приёмами отличает новичка от того, кто пишет по-настоящему гибкий код.

В этом уроке вы узнаете все способы, которыми Python позволяет передавать данные в функции: от простых позиционных аргументов до мощных приёмов *args и **kwargs, которые используются в настоящих библиотеках.

Позиционные и именованные аргументы

Когда вы вызываете функцию, Python сопоставляет аргументы с параметрами двумя способами. Позиционные аргументы сопоставляются по их позиции: первый аргумент попадает в первый параметр. Именованные аргументы сопоставляются по имени.

Позиционные и именованные аргументы
def describe_pet(animal, name):
print(name + ' is a ' + animal)
# Positional: order matters
describe_pet('cat', 'Whiskers')
# Keyword: order doesn't matter
describe_pet(name='Buddy', animal='dog')
Вывод
Whiskers is a cat
Buddy is a dog

Именованный аргумент записывается как parameter_name=value. Такой вызов сам себя документирует: любой, кто читает код, точно видит, что означает каждое значение, даже не заглядывая в определение функции.

В одном вызове можно сочетать оба стиля, но позиционные аргументы должны идти перед именованными.

Позиционные вместе с именованными
def describe_pet(animal, name, age):
print(name + ' is a ' + str(age) + '-year-old ' + animal)
# Positional first, then keyword
describe_pet('cat', name='Whiskers', age=3)
Вывод
Whiskers is a 3-year-old cat

Как значения по умолчанию делают функции гибкими

Значения по умолчанию позволяют объявлять необязательные параметры. Если вызывающий код передал значение, используется оно. Если нет, в дело вступает значение по умолчанию.

Значения по умолчанию в действии
def make_coffee(size, milk='regular', sugar=0):
order = size + ' coffee'
if milk != 'regular':
order += ' with ' + milk + ' milk'
if sugar > 0:
order += ', ' + str(sugar) + ' sugar(s)'
print(order)
make_coffee('large')
make_coffee('medium', 'oat')
make_coffee('small', 'almond', 2)
Вывод
large coffee
medium coffee with oat milk
small coffee with almond milk, 2 sugar(s)

У параметра size нет значения по умолчанию, поэтому он обязателен. У параметров milk и sugar значения по умолчанию есть, так что они необязательны. Именно так устроены многие настоящие библиотеки Python: несколько обязательных параметров и много необязательных с разумными значениями по умолчанию.

Ловушка изменяемого значения по умолчанию

Это один из самых известных подвохов Python. Если в качестве значения по умолчанию использовать изменяемый объект (например, список), этот объект создаётся один раз при определении функции, а не при каждом её вызове.

Это значит, что все вызовы работают с одним и тем же списком. Изменения из одного вызова переходят в следующий. Посмотрим, как это выглядит:

Ловушка изменяемого значения по умолчанию
def add_item(item, shopping_list=[]):
shopping_list.append(item)
return shopping_list
print(add_item('apples'))
print(add_item('bread'))
print(add_item('milk'))
Вывод
['apples']
['apples', 'bread']
['apples', 'bread', 'milk']

Удивлены? Каждый вызов должен был начинаться с нового списка, а список всё растёт. Так происходит потому, что [] создаётся один раз, когда функция определяется, и затем используется при каждом вызове.

Исправить это помогает известный в Python приём: используйте None как значение по умолчанию и создавайте новый список внутри функции.

Решение: None в качестве значения по умолчанию
def add_item(item, shopping_list=None):
if shopping_list is None:
shopping_list = []
shopping_list.append(item)
return shopping_list
print(add_item('apples'))
print(add_item('bread'))
print(add_item('milk'))
Вывод
['apples']
['bread']
['milk']

Что делает *args?

Иногда заранее неизвестно, сколько аргументов получит функция. Например, вам нужна функция, которая складывает любое количество чисел: 2, 5 или 100.

Синтаксис *args собирает все лишние позиционные аргументы в кортеж. Затем их можно перебрать в цикле, обратиться к ним по индексу или сделать с ними всё, что делают с кортежем.

*args, чтобы принимать любое число аргументов
def add_all(*numbers):
total = 0
for n in numbers:
total += n
return total
print(add_all(1, 2, 3))
print(add_all(10, 20, 30, 40))
print(add_all(5))
Вывод
6
100
5

Имя args всего лишь соглашение. Можно написать *numbers, *items или *values. Вся магия в *: звёздочка говорит Python упаковать все оставшиеся позиционные аргументы в кортеж.

Заглянем внутрь *args
def show_args(*args):
print(type(args))
print(args)
for i, arg in enumerate(args):
print('Argument ' + str(i) + ': ' + str(arg))
show_args('hello', 42, True)
Вывод
<class 'tuple'>
('hello', 42, True)
Argument 0: hello
Argument 1: 42
Argument 2: True

*args можно сочетать с обычными параметрами. Сначала свои значения забирают обычные параметры, а затем *args собирает всё, что осталось.

Обычный параметр + *args
def greet(greeting, *names):
for name in names:
print(greeting + ', ' + name + '!')
greet('Hello', 'Alice', 'Bob', 'Charlie')
Вывод
Hello, Alice!
Hello, Bob!
Hello, Charlie!

Что делает **kwargs?

Если *args собирает лишние позиционные аргументы, то **kwargs собирает лишние именованные аргументы в словарь. Ключами становятся имена аргументов, а значениями их значения.

Как использовать **kwargs
def build_profile(**info):
for key, value in info.items():
print(key + ': ' + str(value))
build_profile(name='Alice', age=25, city='Paris')
Вывод
name: Alice
age: 25
city: Paris

Как и args, имя kwargs всего лишь соглашение. Можно написать **info, **options или **settings. Главное здесь **: две звёздочки говорят Python упаковать лишние именованные аргументы в словарь.

Это невероятно удобно для функций, которые должны быть гибкими. Вы можете передавать любые именованные данные, не меняя определение функции.

Обычный параметр + **kwargs
def create_user(username, **details):
print('Username: ' + username)
for key, value in details.items():
print(' ' + key + ': ' + str(value))
create_user('alice_99', email='alice@mail.com', age=25)
print()
create_user('bob_42', city='London')
Вывод
Username: alice_99
  email: alice@mail.com
  age: 25

Username: bob_42
  city: London

Все виды аргументов вместе

В одной функции можно использовать обычные параметры, значения по умолчанию, *args и **kwargs. Но порядок важен. Python требует вот такой последовательности:

  • Обычные позиционные параметры
  • Параметры со значениями по умолчанию
  • *args
  • **kwargs
  • Все виды аргументов в одной функции
    def order_food(restaurant, *items, delivery=False, **extras):
    print('Order from: ' + restaurant)
    print('Items: ' + ', '.join(items))
    print('Delivery: ' + str(delivery))
    if extras:
    for key, value in extras.items():
    print(' ' + key + ': ' + str(value))
    order_food('Pizza Palace', 'margherita', 'garlic bread',
    delivery=True, tip=5, note='extra cheese')
    Вывод
    Order from: Pizza Palace
    Items: margherita, garlic bread
    Delivery: True
      tip: 5
      note: extra cheese
    Слишком гибко (трудно понять)
    def process(*args, **kwargs):
        # What does this expect?
        pass
    Ясно и явно
    def 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)
    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!.

    # Define your function here
    # Test it
    greet('Alice')
    greet('Bob', 'Hey')
    Сумма всех чисел
    Напишите код

    Напишите функцию sum_all, которая с помощью *args принимает любое количество числовых аргументов и возвращает их сумму. Если аргументы не переданы, верните 0.

    # Define your function here
    # Test it
    print(sum_all(1, 2, 3))
    print(sum_all(10, 20))
    print(sum_all())
    Исправьте изменяемое значение по умолчанию
    Исправьте ошибку

    Эта функция должна каждый раз создавать новый список, но в ней есть ошибка с изменяемым значением по умолчанию. Исправьте её так, чтобы каждый вызов начинался с нового списка.

    Ожидаемый вывод:

    [1]
    [2]
    [3]
    def append_to(value, items=[]):
    items.append(value)
    return items
    print(append_to(1))
    print(append_to(2))
    print(append_to(3))
    Соберите строку профиля
    Напишите код

    Напишите функцию build_profile, которая принимает обязательный параметр name и с помощью **kwargs принимает любые дополнительные сведения. Она должна вывести имя в первой строке, а затем каждую пару «ключ: значение» на отдельной строке в формате key: value (с двумя пробелами в начале). Выводите ключи в том порядке, в котором они переданы.

    Пример: build_profile('Alice', age=25, city='NYC') должна вывести:

    Alice
      age: 25
      city: NYC
    # Define your function here
    # Test it
    build_profile('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.

    # Define your function here
    # Test it
    print(calc('add', 1, 2, 3))
    print(calc('multiply', 2, 3, 4))
    print(calc('max', 5, 2, 8, 1))
    Перепишите функцию с **kwargs
    Улучшите код

    Перепишите функцию ниже так, чтобы вместо отдельных параметров она использовала **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
    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)

    Похожие уроки

    Урок был полезен?