К примеру, что делает *
(астериск) в следующем коде:
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]