Что значит * (звёздочка) и ** двойная звёздочка в Питоне?

1,00
р.
К примеру, что делает * (астериск) в следующем коде:
print(*min(p for p in counter.items() if p[1] == max_count))
print(*team, *coef)
seasons = [datetime(*args) for args in [ (Y, 1, 1), # winter (Y, 3, 1), # spring (Y, 6, 1), # summer (Y, 9, 1), # autumn (Y, 12, 1) # winter ]]
def lcm(*args): """Least common multiple.""" return reduce(lambda a, b: a * b // gcd(a, b), args)
async def watchdog(loop, last_activity_time, timeout, func, *args): "Run *func(*args)* if more than *timeout* seconds since *last_activity_time*." while (loop.time() - last_activity_time()) < timeout: await asyncio.sleep(1) return func(*args)
и что делают две ** звёздочки:
'{a:.{n}f}'.format(**vars())
class A: def __init__(self, a, **kwargs): super().__init__(**kwargs)

Ответ
Звёздочка в Питоне помимо умножения x*y (help('*')) и возведения в степень x**y (help('**'))† используется, чтобы обозначить ноль или более чего-либо.
К примеру в описании параметров функции:
def f(*args): print(args)
* означает, что функция принимает ноль или более аргументов, которые доступны внутри функции в виде кортежа args:
>>> f(1,'a') (1, 'a')
Для именованных параметров используются две звёздочки:
def g(a, b, *args, name='default', **kwargs): print(a, b, args, name, kwargs)
здесь g() принимает два обязательных аргумента и произвольное (ноль или более) количество других аргументов:
>>> g(1, b=2, c=3) 1 2 () default {'c': 3}
kwargs—это словарь дополнительных аргументов, переданных по имени (c в данном случае). А args это пустой кортеж (), так как дополнительных позиционных аргументов не было передано.
После * все параметры обязаны передаваться по имени, пример:
def min_item(items, *, key=lambda x: x): ...
При вызове, если задан, key обязан быть указан по имени: min([1,2,-3], key=abs).
Принятие произвольного количества аргументов может быть полезно при создании функций-обёрток:
def my_print(*args, **kwargs): flush = kwargs.pop('flush', True) # flush unless overriden print(*args, flush=flush, **kwargs)
При множественном наследовании **kwargs помогает реализовать требование совместимости параметров для методов базовых классов, так как kwargs позволяет передать произвольные именованные аргументы.
Видно, что звёздочку можно использовать и при вызове функции:
L = [1, 2, 3] s = "abc" print(*L, *s) # iterable unpacking: print(1, 2, 3, 'a', 'b', 'c') # -> 1 2 3 a b c
произвольные коллекции (iterable в общем случае) L, s распаковываются и каждый их элемент передаётся в виде отдельного агрумента в вызываемую функцию (print()).
Можно использовать и при явном присваивании:
>>> first, *middle, last = L >>> first, middle, last (1, [2], 3)
в этом случае первый и последний аргументы из списка L распаковываются в явно приведённые имена (first, last), а остаток ноль или более элементов в виде списка помещаются в middle.
Звёздочку можно использовать и при задании списков, кортежей, наборов и словарей в исходном коде, используя соответствующий синтаксис (tuple, list, set, and dictionary displays):
>>> *range(4), 4 (0, 1, 2, 3, 4) >>> [*range(4), 4] [0, 1, 2, 3, 4] >>> {*range(4), 4} {0, 1, 2, 3, 4} >>> {'x': 1, **{'y': 2}} # dictionary unpacking inside dictionary display {'x': 1, 'y': 2}
Тонкий момент: запятая в Питоне создаёт кортеж—скобки нужны только для пустого кортежа (). Поэтому первая строчка равнозначна: (*range(4), 4).
Так же как и при вызове функций, звёздочка распаковывает коллекцию здесь и действует как будто каждый элемент был передан отдельно в соответствующие конструкторы.
Таким образом можно сложить два словаря или произвольные отображения (Mapping):
>>> a = {'a': 1, 'b': 2} >>> b = {'a': 3, 'c': 0} >>> {**a, **b} {'a': 3, 'b': 2, 'c': 0} >>> {**b, **a} {'a': 1, 'c': 0, 'b': 2}
При наличии дублирующих ключей, более поздние значения побеждают, как обычно: {'a': 1, 'a': 3} == {'a': 3}.
Знание, что делает звёздочка полезно, чтобы объяснить как zip(*matrix) транспонирует квадратную матрицу или как обойти итератор по ровно n элементов за раз: zip(*[iterator]*n).
Помимо указанных значений, звёздочка может присутствовать в имени файла для создания шаблона (wildcard), к примеру:
from pathlib import Path
print(*Path().glob('*.py')) print(*Path().glob('**/*.py'))
Первый print() печатает через пробел все (ноль или более) имена файлов в текущей директории с расширением .py. Подобный (*.py) синтаксис популярен, так как он используется в командной строке (shell). Второй print() с двумя звёздочками ('**/*.py') выводит имена Питон-файлов во всем дереве директорий (включая вложенные директории).
В регулярных выражениях * означает повторение ноль или более раз:
import re
if re.fullmatch(r'x*', text): print('текст пустой или содержит только `x`')

† умножение натуральных чисел n*m можно рассматривать как повторение сложения (ноль или более) раз. Аналогично, возведение в степень n**m можно рассматривать как повторение умножения:
2 * 3 == 2 + 2 + 2 2 ** 3 == 2 * 2 * 2 [2] * 3 == [2, 2, 2]