*
и **
-операторы в вызовах функций синтаксически идентичны им в определениях, но выполняют обратное действие. Посмотрим на примере функции sum()
:
def sum(*numbers):
result = 0
for num in numbers:
result += num
return result
Вызовем sum()
, применив *
-оператор к списку аргументов:
numbers = [1, 7, 4]
sum(*numbers) # 12
Здесь, знакомый нам *
-оператор, уже распаковывает список на аргументы. Количество полученных аргументов равно количеству элементов массива. По сути, код выше преобразуется в вызов:
sum(numbers[0], numbers[1], numbers[2])
# sum(1, 7, 4)
Как и в случае с определением функций, *
-оператор может использоваться совместно с позиционными аргументами:
numbers = [1, 7, 4]
sum(8, *numbers) # 20
sum(8, 10, *numbers) # 30
sum(8, 10, 70, *numbers) # 100
В отличие от оператора упаковки в определении функций, оператор распаковки не обязательно должен быть последним, он может располагаться в любой позиции:
numbers = [1, 7, 4]
sum(8, 10, *numbers) # 30
sum(8, *numbers, 10) # 30
sum(*numbers, 8, 10) # 30
Более того, может быть любое количество операторов распаковки и в любом порядке:
numbers1 = [1, 7, 4]
numbers2 = [5, 5]
# sum(1, 7, 4, 5, 5)
sum(*numbers1, *numbers2) # 22
# sum(5, 5, 1, 7, 4)
sum(*numbers2, *numbers1) # 22
# sum(8, 1, 7, 4, 10, 5, 5)
sum(8, *numbers1, 10, *numbers2) # 40
Распаковка именованных параметров
По аналогии с оператором распаковки позиционных параметров работает и оператор распаковки именованных - **
-оператор.
def func(**kwargs):
for key, value in kwargs.items():
print('key = ', key)
print('value = ', value)
d = {'a': 42, 'b': 'python'}
# распаковываем словарь
func(**d)
# key = a
# value = 42
# key = b
# value = python
Важно, что в примере выше, если мы забудем распаковать словарь, то получим ошибку. Ведь функция принимает не словарь, а любое количество именованных аргументов. Но **
-оператор распаковывает переданный словарь в именованные аргументы вида a=42, b='python'
. Затем уже оператор упаковки, собирает их в словарь kwargs
, который мы уже и обрабатываем в функции.
def func(**kwargs):
for key, value in kwargs.items():
print('key = ', key)
print('value = ', value)
d = {'a': 42, 'b': 'python'}
func(d) # TypeError: func() takes 0 positional arguments but 1 was given
Оператор **
упаковки-распаковки удобно использовать в функциях, принимающих неограниченное количество опций, например, функциях конфигурации:
def get_config(**options):
default_options = {'case': 'lower', 'output': 'console'}
# возвращаем новые опции
return default_options | options
get_config() # {'case': 'lower', 'output': 'console'}
get_config(**{'case': 'upper'}) # {'case': 'upper', 'output': 'console'}
get_config(**{'case': 'upper', 'code': 'utf-8'})
# {'case': 'upper', 'output': 'console', 'code': 'utf-8'}
Итог
Оба оператора упаковки и распаковки обозначаются символами *
и **
. Чтобы их не путать при работе с функциями, нужно следовать следующим принципам:
- оператор упаковки используется при создании функций. Он позволяет собрать, упаковывать, остаточные аргументы функции
- оператор распаковки используется при вызове функций. Он извлекает элементы из коллекции и заполняет ими параметры функции при вызове
Сочетая эти операторы мы можем создавать функции работающие с любым числом аргументов.
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
- Статья «Как учиться и справляться с негативными мыслями»
- Статья «Ловушки обучения»
- Статья «Сложные простые задачи по программированию»
- Вебинар «Как самостоятельно учиться»
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.