Все, что вам нужно знать о звездочках в Python

Большинство разработчиков знают символ звездочки (*) как оператор умножения в Python:

product = 4 * 2  # 8
Вход в полноэкранный режим Выход из полноэкранного режима

Однако звездочка имеет особое значение для списковых или словарных структур данных.

*args и **kwargs

*args в определении функции

Наиболее известное применение оператора звездочки, также известного как оператор деструктуризации, в Python — это использование его в функциях.

Допустим, у вас есть функция, которая может складывать значения и возвращает сумму этих значений:

def add(number_1, number_2):
    return number_1 + number_2

print(add(1,2)) # 3
Войти в полноэкранный режим Выйти из полноэкранного режима

Что если вы хотите просуммировать произвольное количество значений? Вы можете добавить звездочку перед именем аргумента, как показано ниже:

def add(*numbers):
    sum = 0
    for number in numbers:
        sum += number
    return sum
Ввести полноэкранный режим Выйти из полноэкранного режима

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

add(1, 2, 3, 4) # 10
Войти в полноэкранный режим Выйти из полноэкранного режима

* в вызове функции

До сих пор мы видели, что можно определить функцию так, чтобы она принимала список параметров. Но что если у нас есть функция с фиксированным набором аргументов, и мы хотим передать ей список значений. Рассмотрим эту функцию:

def add(number_1, number_2, number_3):
    return number_1 + number_2 + number_3
Войти в полноэкранный режим Выйти из полноэкранного режима

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

my_list = [1, 2, 3]

add(my_list[0], my_list[1], my_list[2])
Ввести полноэкранный режим Выйти из полноэкранного режима

К счастью, оператор деструктуризации (*) работает в обоих направлениях. Мы уже видели его использование в определении функции, но мы можем использовать его и для вызова функции:

my_list = [1, 2, 3]

add(*my_list)
Войти в полноэкранный режим Выйти из полноэкранного режима

**kwargs в определении функции

Точно так же, как мы можем деструктурировать списки с помощью оператора asterisk (*), мы можем использовать оператор double-asterisk (**) для деструктуризации словарей в функциях Python.

Рассмотрим такую функцию:

def change_user_details(username, email, phone, date_of_birth, street_address):
    user = get_user(username)
    user.email = email
    user.phone = phone
    ...
Войти в полноэкранный режим Выйти из полноэкранного режима

Если мы вызываем ее с аргументами в виде ключевых слов (kwargs), вызов функции может выглядеть следующим образом:

change_user_details('bascodes', email='blog@bascodes.example.com', phone='...', ...)
Войти в полноэкранный режим Выйти из полноэкранного режима

В этом случае мы можем переписать нашу функцию так, чтобы она принимала произвольное количество ключевых аргументов, которые затем представляются в виде словаря kwargs:

def change_user_details(username, **kwargs):
    user = get_user(username)
    user.email = kwargs['email']
    user.phone = kwargs['phone']
    ...
Войти в полноэкранный режим Выйти из полноэкранного режима

Конечно, мы можем использовать словарь kwargs как любой другой словарь в Python, так что функция может стать немного чище, если мы действительно используем структуру данных словаря следующим образом:

def change_user_details(username, **kwargs):
    user = get_user(username)
    for attribute, value in kwargs:
        setattr(user, attribute, value)
    ...
Войти в полноэкранный режим Выйти из полноэкранного режима

**kwargs в определении функции

Конечно, оператор ** работает и для вызова функции:

details = {
    'email': 'blog@bascodes.example.com',
    ...
}
change_user_detail('bascodes', **details)
Войти в полноэкранный режим Выход из полноэкранного режима

Ограничение вызова функций

Только аргументы ключевых слов

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

def my_function(*, keyword_arg_1):
    ...
Войти в полноэкранный режим Выйти из полноэкранного режима

Но что в этом случае делает автономная звездочка? Звездочка перехватывает все (не ключевые слова) аргументы в списке, как мы видели выше. В нашем случае нет имени переменной, которое могло бы попасть в список. После * у нас есть переменная keyword_arg_1. Поскольку * уже сопоставил все позиционные аргументы, у нас остается keyword_arg_1, который должен быть использован в качестве аргумента ключевого слова.

Вызов приведенной выше функции с my_function(1) приведет к ошибке:

TypeError: my_function() takes 0 positional arguments but 1 was given
Вход в полноэкранный режим Выйти из полноэкранного режима

Только позиционные аргументы

Что если мы хотим заставить пользователей нашей функции использовать только позиционные аргументы — в отличие от аргументов с ключевыми словами в предыдущем примере?

Ну, есть очень питонический способ. Мы интерпретируем знак / (противоположный умножению), чтобы сделать этот трюк:

def only_positional_arguments(arg1, arg2, /):
    ...
Войти в полноэкранный режим Выход из полноэкранного режима

Удивительно мало разработчиков Python знают об этом трюке, который был введен в Python 3.8 через PEP 570.

Если вы вызовете последнюю функцию с only_positional_arguments(arg1=1, arg2=2), это вызовет ошибку TypeError:

TypeError: only_positional_arguments() got some positional-only arguments passed as keyword arguments: 'arg1, arg2'
Вход в полноэкранный режим Выход из полноэкранного режима

Использование * и ** в литералах

Операторы звездочка (*) и двойная звездочка (**) работают не только для определения и вызова функций, но могут использоваться для построения списков и словарей.

Конструирование списков

Допустим, у нас есть два списка и мы хотим их объединить.

my_list_1 = [1, 2, 3]
my_list_2 = [10, 20, 30]
Войдите в полноэкранный режим Выход из полноэкранного режима

Конечно, мы можем объединить эти списки с помощью оператора +:

merged_list = my_list_1 + my_list_2
Войти в полноэкранный режим Выйти из полноэкранного режима

Однако оператор * дает нам немного больше гибкости. Допустим, мы хотим включить скалярное значение в середину, мы можем использовать:

some_value = 42
merged_list = [*my_list_1, some_value, *my_list_2]

# [1, 2, 3, 42, 10, 20, 30]
Войти в полноэкранный режим Выйти из полноэкранного режима

Создание словарей

Опять же, то, что верно для списков и оператора звездочка (*), верно для словарей и оператора двойная звездочка (**).


social_media_details = {
    'twitter': 'bascodes'
}

contact_details = {
    'email': 'blog@bascodes.example.com'
}

user_dict = {'username': 'bas', **social_media_details, **contact_details}
# {'email': 'blog@bascodes.example.com', 'twitter': 'bascodes', 'username': 'bas'}
Вход в полноэкранный режим Выход из полноэкранного режима

Оцените статью
Procodings.ru
Добавить комментарий