Что означает звездный оператор?

Что это * оператор означает в Python, например, в коде, как zip(*x) или же f(**k)?

  1. Как это обрабатывается внутри переводчика?
  2. Влияет ли это на производительность вообще? Это быстро или медленно?
  3. Когда это полезно, а когда нет?
  4. Должно ли оно использоваться в объявлении функции или в вызове?

5 ответов

Решение

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

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 arguments, это позволяет функции принимать любое количество позиционных и / или именованных аргументов, которые не указаны в объявлении.

Пример:

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

Один маленький момент: это не операторы. Операторы используются в выражениях для создания новых значений из существующих значений (например, 1+2 становится 3. Здесь * и ** являются частью синтаксиса объявлений функций и вызовов.

При вызове функции одиночная звезда превращает список в отдельные аргументы (например, 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) означает "бар принимает произвольное количество аргументов ключевого слова, и они будут доступны через словарь k (то есть, если пользователь вызывает bar(x=42, y=23), k будет {'x': 42, 'y': 23})".

Я считаю это особенно полезным, когда вы хотите "сохранить" вызов функции.

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

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)

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

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

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

а также:

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

Другие вопросы по тегам