История вопроса:
Операторы * и ** для распаковки появились в Python давно, но их применение расширялось с каждой версией (например, с Python 3.5 добавлена поддержка объединения нескольких коллекций через * и **). Эти операторы делают работу с коллекциями гибче, в том числе при передаче аргументов и сборе их в функции.
Проблема:
Без распаковки при работе с динамическими последовательностями, передачей параметров с переменным количеством аргументов, возникает необходимость вручную писать циклы и проверять размерность коллекций. Ошибки легко появляются при некорректном использовании или если не различать назначение * и **.
Решение:
Оператор * предназначен для распаковки последовательностей (list, tuple, set), а оператор ** — для распаковки словарей при вызове функций или объединении нескольких словарей. Они позволяют элегантно передавать аргументы, делать слияние коллекций, легко превращать произвольные структуры в параметры функции.
Пример кода:
def foo(a, b, c): print(a, b, c) args = (1, 2, 3) foo(*args) # 1 2 3 params = {'a': 10, 'b': 20, 'c': 30} foo(**params) # 10 20 30 list1 = [1, 2] list2 = [3, 4] combined = [*list1, *list2] print(combined) # [1, 2, 3, 4]
Ключевые особенности:
*sequence передает элементы как отдельные позиционные аргументы, **dict как именованные аргументы.[*a, *b] и {**d1, **d2}.Можно ли использовать * и ** для смешанных коллекций?
При вызове функции * работает только с позиционными, а ** только с именованными аргументами. Если передать нераспакованный dict как * или последовательность как **, возникнет ошибка.
def foo(a, b): print(a, b) foo(*{'a': 1, 'b': 2}) # Печатает: a b (ключи словаря, не значения!)
**Что будет, если имена ключей пересекаются при объединении dict через {**d1, d2}?
В результате будет значение из последнего словаря с таким ключом.
d1 = {'x': 1, 'y': 2} d2 = {'y': 33, 'z': 44} merged = {**d1, **d2} print(merged) # {'x': 1, 'y': 33, 'z': 44}
Можно ли использовать * и ** внутри списковых или словарных выражений?
Да, это законно начиная с Python 3.5, например:
lst = [1, 2, *range(3, 6)] # [1, 2, 3, 4, 5] dct = {**{'a': 1}, 'b': 2, **{'c': 3}}
Девелопер принимает во входную функцию dict и разбирает его с помощью * вместо **. Происходит неожиданное поведение: в функцию попадают не значения, а ключи.
Плюсы:
Код не падает сразу, кажется “работающим”.
Минусы:
Скрытые ошибки и несоответствие ожидаемой логики.
Корректная передача параметров через **kwargs, аккуратное слияние словарей, использование * при динамическом объединении последовательностей.
Плюсы:
Максимальная гибкость, лаконичность кода, простота рефакторинга.
Минусы:
При большом числе параметров и коллекций важно внимательно следить за именами и порядком, иначе возможны ошибки.