Что означает звездный оператор при вызове функции?
Что означает оператор *
в Python, например, в коде, подобном zip(*x)
или f(**k)
?
- Как это обрабатывается внутри переводчика?
- Влияет ли это на производительность вообще? Это быстро или медленно?
- Когда это полезно, а когда нет?
- Должно ли оно использоваться в объявлении функции или в вызове?
Ответы
Ответ 1
Единственная звезда *
распаковывает последовательность/коллекцию в позиционные аргументы, поэтому вы можете сделать это:
def sum(a, b):
return a + b
values = (1, 2)
s = sum(*values)
Это распакует кортеж так, чтобы он выполнялся как:
s = sum(1, 2)
Двойная звезда **
делает то же самое, только используя словарь и, следовательно, названные аргументы:
values = { 'a': 1, 'b': 2 }
s = sum(**values)
Вы также можете комбинировать:
def sum(a, b, c, d):
return a + b + c + d
values1 = (1, 2)
values2 = { 'c': 10, 'd': 15 }
s = sum(*values1, **values2)
будет выполняться как:
s = sum(1, 2, c=10, d=15)
Также см. раздел 4.7.4 - Распаковка списков аргументов документации Python.
Кроме того, вы можете определить функции для принятия аргументов *x
и **y
, это позволяет функции принимать любое количество позиционных и/или именованных аргументов, которые специально не указаны в объявлении.
Пример:
def sum(*values):
s = 0
for v in values:
s = s + v
return s
s = sum(1, 2, 3, 4, 5)
или с помощью **
:
def get_a(**values):
return values['a']
s = get_a(a=1, b=2) # returns 1
это может позволить вам указать большое количество необязательных параметров без их объявления.
И снова вы можете комбинировать:
def sum(*values, **options):
s = 0
for i in values:
s = s + i
if "neg" in options:
if options["neg"]:
s = -s
return s
s = sum(1, 2, 3, 4, 5) # returns 15
s = sum(1, 2, 3, 4, 5, neg=True) # returns -15
s = sum(1, 2, 3, 4, 5, neg=False) # returns 15
Ответ 2
Одна маленькая точка: это не операторы. Операторы используются в выражениях для создания новых значений из существующих значений (например, 1 + 2 становится 3. Например, * и ** являются частью синтаксиса деклараций и вызовов функций.
Ответ 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.
и
Если выражение функции синтаксиса ** появляется в вызове функции, выражение должно оцениваться с помощью сопоставления, содержимое которого рассматривается как дополнительные аргументы ключевого слова. В случае ключевого слова, появляющегося в обоих выражениях и в качестве явного аргумента ключевого слова, возникает исключение TypeError.
Ответ 5
В вызове функции одиночная звезда превращает список в отдельные аргументы (например, zip(*x)
совпадает с zip(x1,x2,x3)
, если x=[x1,x2,x3]
), а двойная звезда превращает словарь в отдельные аргументы ключевого слова (например, f(**k)
то же, что и f(x=my_x, y=my_y)
, если 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}
)".