Операторы * и ** для упаковки и распаковки коллекций

Курс по Python: https://stepik.org/course/100707

На этом занятии я хочу немного отступить от темы функций и рассказать об операторах * и **, которые мы затронули на предыдущем уроке.

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

x, y = (1, 2)

то его можно распаковать в две переменные. Но, если мы пропишем там больше значений, например, четыре:

x, y = (1, 2, 3, 4)

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

x, *y = (1, 2, 3, 4)

или, в первую, без разницы:

*x, y = (1, 2, 3, 4)

То же самое можно проделывать и со списками:

x, *y = [1, "a", True, 4]

и строками:

*x, y, z = "Hello Python!"

И вообще с любыми итерируемыми объектами. То есть, оператор * упаковывает оставшиеся значения в список. Правда, мы не можем упаковывать уже упакованные данные, например, так:

*y = 1, 2, 3

произойдет ошибка, но вот так:

x, *y = 1, 2, 3

уже будет работать.

Этот же оператор может выполнять и обратную операцию – распаковывать коллекции в набор данных. Пусть у нас имеется список:

a = [1, 2, 3]

И на его основе мы хотим сформировать кортеж. Если просто записать переменную в круглых скобках:

(a,)

то увидим кортеж со списком внутри. Но, если прописать оператор * перед списком:

(*a,)

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

d = -5, 5

и вызовем с этими значениями функцию:

range(d)

Возникнет ошибка, так как функция ожидает числа в качестве аргументов, а не коллекции. Но, мы можем распаковать кортеж d в два числа, поставив перед ним оператор *:

range(*d)

и теперь никаких ошибок нет. Давайте посмотрим, что вернет эта функция, преобразуем все к списку:

list(range(*d))

Да, получаем ожидаемые значения от -5 до 5. И, теперь, это же преобразование генератора range() в список мы можем сделать и так:

[*range(*d)]

Здорово, да?! Мы оператором * распаковали итерируемый объект и составили из его значений список.

Мало того, мы таким образом можем делать объединение разных коллекций в одну коллекцию, например, список:

[*range(*d), *(True, False), *a]

Как видите, оператор * - невероятно удобный инструмент. И те же самые действия можно делать и со словарем. Зададим, следующий словарь с расшифровкой оценок:

d = {0: "безнадежно", 1: "убого", 2: "неуд.", 3: "удовл.", 4: "хорошо", 5: "отлично"}

Распаковать его можно двумя способами. Если прописать один оператор *:

{*d}

То получим множество, состоящее из ключей этого словаря. Или, можно сформировать список из ключей:

[*d]

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

[*d.values()]

и получим список из значений. Соответственно, метод:

[*d.items()]

вернет кортежи из пары ключ-значение.

Если же требуется распаковать словарь как словарь, то перед ним следует прописать две звездочки:

{**d}

Теперь вместо множества мы получаем словарь. Где это нам может пригодиться? Например, для объединения нескольких словарей в один. Создадим еще один словарь:

d2 = {6: "превосходно", 7: "элитарно", 8: "божественно"}

И соединим их через распаковку данных:

{**d, **d2}

На выходе получаем новый словарь с объединенными данными. Такой прием часто используется на практике, когда нужно объединить сразу несколько словарей. А вот для упаковки оператор ** не используется, если прописать, что то вроде:

a, b, **c = d

то получим синтаксическую ошибку. Можно указывать только одну звездочку. Исключение только параметр **kwargs при определении функций.

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

Курс по Python: https://stepik.org/course/100707

Видео по теме