Вопрос-Ответ

What do ** (double star/asterisk) and * (star/asterisk) mean in a function call?

Что означают ** (двойная звезда / asterisk) и * (звезда / asterisk) в вызове функции?

Что означают zip(*x) и f(**k) соответственно в коде типа * or **? Как Python реализует это поведение и каковы последствия для производительности?


Смотрите также: Преобразование кортежей в аргументы. Пожалуйста, используйте это, чтобы закрыть вопросы, где OP должен использовать * аргумент и не знает о его существовании. Аналогичным образом используйте преобразование Python dict в kwargs? для случая использования **.

Смотрите Что ** (двойная звезда / asterisk) и * (звезда / asterisk) делают для параметров? дополнительный вопрос о параметрах.

Переведено автоматически
Ответ 1

Одиночная звездочка * распаковывает последовательность или коллекцию в позиционные аргументы. Предположим, у нас есть

def add(a, b):
return a + b

values = (1, 2)

Используя * оператор распаковки, мы можем записать s = add(*values), что будет эквивалентно записи s = add(1, 2).

Двойная звездочка ** делает то же самое для словаря, предоставляя значения для именованных аргументов:

values = { 'a': 1, 'b': 2 }
s = add(**values) # equivalent to add(a=1, b=2)

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

def sum(a, b, c, d):
return a + b + c + d

values1 = (1, 2)
values2 = { 'c': 10, 'd': 15 }

тогда s = add(*values1, **values2) эквивалентно s = sum(1, 2, c=10, d=15).

Смотрите также соответствующий раздел руководства в документации Python.


Аналогично, * и ** могут использоваться для параметров. Использование * позволяет функции принимать любое количество позиционных аргументов, которые будут собраны в один параметр:

def add(*values):
s = 0
for v in values:
s = s + v
return s

Теперь, когда функция вызывается как s = add(1, 2, 3, 4, 5), values будет кортеж (1, 2, 3, 4, 5) (который, конечно, выдает результат 15).

Аналогично, параметр, отмеченный **, получит dict:

def get_a(**values):
return values['a']

s = get_a(a=1, b=2) # returns 1

это позволяет указывать большое количество необязательных параметров без необходимости их объявления.

Опять же, и то, и другое можно комбинировать:

def add(*values, **options):
s = 0
for i in values:
s = s + i
if "neg" in options:
if options["neg"]:
s = -s
return s

s = add(1, 2, 3, 4, 5) # returns 15
s = add(1, 2, 3, 4, 5, neg=True) # returns -15
s = add(1, 2, 3, 4, 5, neg=False) # returns 15
Ответ 2

При вызове функции одиночная звездочка превращает список в отдельные аргументы (например, zip(*x) совпадает с zip(x1, x2, x3) given x=[x1,x2,x3]), а двойная звездочка превращает словарь в отдельные аргументы ключевого слова (например, f(**k) совпадает с f(x=my_x, y=my_y) given k = {'x':my_x, 'y':my_y}.

В определении функции все наоборот: одиночная звездочка превращает произвольное количество аргументов в список, а двойное начало превращает произвольное количество аргументов ключевого слова в словарь. Например. def foo(*x) означает "foo принимает произвольное количество аргументов, и они будут доступны через x (т.Е. Если пользователь вызовет foo(1,2,3), x будет (1, 2, 3))" и def bar(**k) означает "bar принимает произвольное количество аргументов ключевого слова, и они будут доступны через k ( т.е. если пользователь позвонит bar(x=42, y=23), k будет {'x': 42, 'y': 23})".

Ответ 3

Я нахожу это особенно полезным для хранения аргументов для вызова функции.

Например, предположим, у меня есть несколько модульных тестов для функции 'add':

def add(a, b):
return a + b

tests = { (1,4):5, (0, 0):0, (-1, 3):3 }

for test, result in tests.items():
print('test: adding', test, '==', result, '---', add(*test) == result)

Нет другого способа вызвать add, кроме ручного выполнения чего-то вроде add(test[0], test[1]), что некрасиво. Кроме того, при наличии переменного количества переменных код может получиться довольно уродливым со всеми необходимыми if-операторами.

Еще одно место, где это полезно, - это определение объектов Factory (объектов, которые создают объекты для вас). Предположим, у вас есть некоторый класс Factory , который создает объекты Car и возвращает их. Вы могли бы сделать так, чтобы это myFactory.make_car('red', 'bmw', '335ix') создавало Car('red', 'bmw', '335ix'), а затем возвращало его.

def make_car(*args):
return Car(*args)

Это также полезно, когда вы хотите вызвать конструктор суперкласса.

Ответ 4

Это называется расширенным синтаксисом вызова. Из документации:


Если синтаксическое выражение * появляется в вызове функции, выражение должно вычисляться в последовательности. Элементы из этой последовательности обрабатываются так, как если бы они были дополнительными позиционными аргументами; если есть позиционные аргументы x1, ..., xN, и выражение вычисляет последовательность y1, ..., yM , это эквивалентно вызову с M + N позиционными аргументами x1, ..., xN, y1, ..., yM .


и:


Если синтаксическое выражение ** появляется в вызове функции, expression должно соответствовать отображению, содержимое которого обрабатывается как дополнительные аргументы ключевого слова. В случае, если ключевое слово появляется как в выражении, так и в качестве явного аргумента ключевого слова, возникает исключение TypeError.


2023-04-27 11:46 python