- Всё, что вам нужно знать о звёздочках в Python
- *args и **kwargs
- *args в объявлении функции
- * при вызове функции
- **kwargs в объявлении функции
- ** в вызове функции
- Ограничение способов вызова функций
- Только именованные аргументы
- Только позиционные аргументы
- Использование * и ** при создании объектов
- Создание списков
- Создание словарей
- Распаковка объектов
- Супер-звезда Python. 5 способов использования оператора *.
- Способ 1: Умножить или возвести в степень
- Способ 2: Любое количество аргументов функции
- Способ 3: Контроль за именованными аргументами
- Ситуация 4: Распаковка объектов
- Ситуация 5: Множественное присваивание
- Эпилог
Всё, что вам нужно знать о звёздочках в Python
Большинство разработчиков знают символ звёздочки как оператор умножения в Python:
Однако, звёздочка имеет особое значение для сложных структур данных, например списка или словаря.
*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 теперь является итерируемым объектом. Действительно, теперь объект numbers стал кортежем, вследствие чего мы можем использовать функцию с произвольным числом аргументов:
* при вызове функции
Мы посмотрели, что можем определить функцию таким образом, чтобы она принимала неопределенное количество аргументов. Но что, если у нас есть функция с фиксированным набором параметров, и мы хотим передать ей список значений? Рассмотрим такой вариант:
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 в объявлении функции
Распаковывать словари с помощью оператора ** можно точно так же, как и делали с * .
Рассмотрим следующую функцию:
def change_user_details(username, email, phone, date_of_birth, street_address): user = get_user(username) user.email = email user.phone = phone .
Если вызывать функцию с именованными аргументами, вызов будет выглядеть следующим образом:
change_user_details('pythontalk', email='pythontalk@ya.ru', 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.items(): setattr(user, attribute, value) .
** в вызове функции
Конечно, оператор ** работает и при вызове функции для распаковки аргументов:
details = < 'email': 'pythontalk@ya.ru', . >change_user_detail('pythontalk', **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 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 = < 'telegram': 'pythontalk' >contact_details = < 'email': 'pythontalk@ya.ru' >user_dict = #
Распаковка объектов
Возможно, вы уже знаете, как разделить элементы последовательности на несколько переменных следующим образом:
my_list = [1, 2, 3] a, b, c = my_list # a -> 1 # b -> 2 # c -> 3
Но знаете ли вы, что звездочки ( * ) можно использовать для назначения переменных, когда у вас есть последовательность произвольной длины?
Скажем, вам нужен первый элемент и последний элемент списка в определенных переменных.
Вы можете сделать это, используя звездочку следующим образом:
my_list = [1, 2, 3] a, *b, c = my_list
В этом примере a теперь содержит первый элемент, а c — последний элемент my_list . А что же в b ?
В b находится весь список, исключая первый и последний элемент, то есть [2] . Обратите внимание, что b теперь является списком.
Эта идея может стать немного яснее, если мы посмотрим на более крупные списки:
my_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] a, *b, c = my_list # a -> 1 # b -> [2, 3, 4, 5, 6, 7, 8, 9] # c -> 10
Супер-звезда Python. 5 способов использования оператора *.
Символ звездочка ( * ) или астериск, известный как оператор умножения, часто используется в языках программирования. Новичкам достаточно знать его основное применение.
Однако, если Вы собираетесь стать экспертом по Python, то нужно углубить свои знания! Пришло время узнать, насколько полезна и мощна “звездочка” в Python.
В этой статье мы рассмотрим 5 способов использования оператора * с примерами, от элементарных к более сложным.
Способ 1: Умножить или возвести в степень
Самое очевидное — использовать звездочки как математические операторы:
Способ 2: Любое количество аргументов функции
Функция не обязательно должна получать фиксированное количество аргументов.
Предположим, что Вы заранее не знаете, сколько аргументов будет передано. Здесь «звездочки» могут прийти к Вам на помощь!
def print_genius(*names): print(type(names)) for n in names: print(n) print_genius('Elon Mask', 'Mark Zuckerberg ', 'Yang Zhou') # class 'tuple' # Elon Mask # Mark Zuckerberg # Yang Zhou def top_genius(**names): print(type(names)) for k, v in names.items(): print(k, v) top_genius(Top1="Elon Mask", Top2="Mark Zuckerberg", Top3="Yang Zhou") # class 'dict' # Top1 Elon Mask # Top2 Mark Zuckerberg # Top3 Yang Zhou
Как показано в приведенном выше примере, при определении функции мы можем определить параметр с префиксом из одной или двух звездочек для передачи неограниченного количества аргументов.
- Параметр с префиксом * может записывать любое количество позиционных аргументов в tuple из аргументов
- Параметр с префиксом ** может записывать любое количество именованных аргументов и составить из них словарь dict
Если число аргументов функции не может быть определено заранее, то мы определяем ее следующим образом:
Вообще говоря, вместо args и kwargs может стоять все, что угодно — это просто общепринятое соглашение. Можете написать tea и coffee . Но понятней от этого не станет 🙂
Способ 3: Контроль за именованными аргументами
Еще одно полезное использование звездочек — заставить функцию принимать только именованные аргументы.
def genius(*, first_name, last_name): print(first_name, last_name) genius('Yang','Zhou') # TypeError: genius() takes 0 positional arguments but 2 were given genius(first_name='Yang', last_name='Zhou') # Yang Zhou
Как показано в приведенном выше примере, одна звездочка * вводит ограничение: все последующие аргументы должны быть именованными.
Если нам нужно ввести ограничение на несколько именованных аргументов и оставить некоторые аргументы позиционными, то нужно просто поставить позиционные аргументы перед звездочкой:
def genius(age, *, first_name, last_name): print(first_name, last_name, 'is', age) genius(28, first_name='Yang', last_name='Zhou') # Yang Zhou is 28
В данном примере, если бы Вы ввели ‘Yang’ без ключевого слова first_name , то Вы получили бы ошибку.
Ситуация 4: Распаковка объектов
Мы можем использовать звездочки для распаковки кортежей, списков, словарей и других итерируемых объектов, что сделает наши программы понятными и элегантными.
Например, если мы собираемся объединить элементы разных итерируемых объектов, например, один список, один кортеж и один набор в новый список, что нам делать?
Очевидно, мы можем использовать циклы for для перебора всех элементов и добавления их в новый список один за другим:
A = [1, 2, 3] B = (4, 5, 6) C = 7, 8, 9> L = [] for a in A: L.append(a) for b in B: L.append(b) for c in C: L.append(c) print(L) # [1, 2, 3, 4, 5, 6, 8, 9, 7]
Таким образом можно выполнить нашу миссию, но код выглядит таким длинным и не очень оптимизированным. Лучший метод — использовать генератор списков:
A = [1, 2, 3] B = (4, 5, 6) C = 7, 8, 9> L = [a for a in A] + [b for b in B] + [c for c in C] print(L) # [1, 2, 3, 4, 5, 6, 8, 9, 7]
Мы сократили три цикла for до одной строки. Это уже хороший код, но способ не самый простой! Можно еще красивей.
Пора посмотреть, насколько красивы в данной ситуации звездочки 🙂
A = [1, 2, 3] B = (4, 5, 6) C = 7, 8, 9> L = [*A, *B, *C] print(L) # [1, 2, 3, 4, 5, 6, 8, 9, 7]
Как показано в примере выше, мы можем использовать звездочку в качестве префикса объектов для распаковки их элементов. Кстати, если мы используем * в качестве префикса к словарю, то будут распакованы его ключи. Если мы используем двойные звездочки ** в качестве префикса, то будут распакованы его значения. Однако, мы должны использовать ключи для получения распакованных значений. Из-за этого неудобства не принято извлекать элементы из словаря помощью звездочек.
D = 'first': 1, 'second': 2, 'third': 3> print(*D) # first second third # print(**D) # TypeError: 'first' is an invalid keyword argument for print() print(',,'.format(**D)) # 1,2,3
Ситуация 5: Множественное присваивание
Этот синтаксис распаковки был введен в PEP 3132, чтобы сделать наш код более элегантным.
Этот PEP позволяет указать «всеобъемлющее» имя, в который будет помещен весь список элементов, которым не присвоено «обычное» имя.
L = [1, 2, 3, 4, 5, 6, 7, 8] a, *b = L print(a) # 1 print(b) # [2, 3, 4, 5, 6, 7, 8]
Эпилог
Звездочка — один из наиболее часто используемых операторов в программировании на любом языке. Однако, в Python много интересных конструкций, которыми можно и нужно пользоваться. Это покажет Ваш профессионализм, а также сделает Ваш код более читаемым и качественным.