Що означає оператор зірка під час виклику функції?


631

Що означає *оператор у Python, наприклад у коді like zip(*x)або f(**k)?

  1. Як внутрішньо обробляється у перекладачі?
  2. Чи це взагалі впливає на продуктивність? Швидко це чи повільно?
  3. Коли це корисно, а коли ні?
  4. Чи слід його використовувати в оголошенні функції або у виклику?


4
Я думаю, це має бути сформульовано як "синтаксис виклику функції". Вони не є операторами, хоча це буде заплутатися так як це* і **оператор , який не має нічого спільного з цим синтаксисом.
Ян Бікінг,

1
@Ian Bicking: Ви повністю праві, * та ** у списку аргументів - це чистий синтаксис (лексеми).
П. Ортіс

2
Примітка: Для PEP 448: Додаткове розпакування узагальнюючих матеріалів (наприклад, [*a, b, *c]або {**d1, **d2}), вам потрібно буде прочитати зірочку в кортежі, визначити список та встановити, подвійну зірочку у визначенні dict , що є специфічним для використання поза викликами функцій та визначеннями функцій . Для попереднього PEP 3132 , див. Призначення багаторазового розпакування в Python, коли ви не знаєте довжину послідовності .
ShadowRanger

1
VTR - це не дублікат того, що ** (подвійна зірка / зірочка) та * (зірка / зірочка) роблять для параметрів? оскільки це питання стосується лише параметрів, хоча відповіді також охоплюють виклики функцій. Зірочку у виклику функції слід позначити як дублікат цього запитання, оскільки воно менш популярне, а відповідь зверху менш повна.
wjandrea

Відповіді:


968

Одиночна зірка *розпаковує послідовність / колекцію в позиційні аргументи, тому ви можете зробити це:

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

4
навіщо вам це потрібно, чи не може функція просто переглядати поданий список, не розширюючи його?
Мартін Беккет

30
Звичайно, але тоді вам доведеться його викликати: s = sum((1, 2, 3, 4, 5))або s = sum([1, 2, 3, 4, 5]), *valuesпараметр робить виклик таким, ніби він приймає ряд аргументів, але вони упаковані до колекції для коду функції.
Лассе В. Карлсен,

12
Ось справжня перевага: ви можете писати функції, які інакше не були б можливими, якщо вам потрібно мати змінну кількість аргументів. Наприклад, функцію printf C, яка має 1 + n аргументів, складно написати як вправу для будь-якого починаючого програміста. У Python новачок може написати def printf (string_template, * args) і рухатися далі.
IceArdor

1
Що станеться, якщо ви (можливо, випадково: p) розпакуєте словник лише одним * замість двох? Здається, щось робить, здається, виходить кортеж, але не так очевидно, що це таке. (редагувати: добре, я думаю, що відповідь полягає в тому, що він просто розпаковує ключі, значення відкидаються)
Бен Фермер

1
Останній приклад передбачає, що * та ** роблять не тільки розпакування, але й упаковку! Дивіться цю чудову сторінку codingame.com/playgrounds/500/…
HCChen

46

Один невеликий момент: це не оператори. Оператори використовуються у виразах для створення нових значень із існуючих значень (наприклад, 1 + 2 стає 3. * І ** тут є частиною синтаксису оголошень та викликів функцій.


7
Зверніть увагу, що документація Python справді викликає * оператор; Я згоден, це вводить в оману.
Крістоф

Дякую. Я шукав чіткого викладу цього в довідковій документації python, і досі цього не бачу. Отже, правило для викликів функцій в основному полягає в тому, що "*" або "**", яке знаходиться на початку виразу у виклику функції, спричиняє таке розширення?
nealmcb

21

Я вважаю це особливо корисним, коли ви хочете "зберегти" виклик функції.

Наприклад, припустимо, у мене є кілька модульних тестів для функції '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, який робить предмети Car і повертає їх. Ви можете зробити так, щоб myFactory.make_car ('red', 'bmw', '335ix') створив Car ('red', 'bmw', '335ix'), а потім повернув його.

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

Це також корисно, коли ви хочете викликати конструктор суперкласу.


4
Мені подобаються ваші приклади. Але, я думаю, -1 + 3 == 2.
екзорцо

5
Я навмисно вклав туди щось, що зазнало б невдачі :)
Дональд Майнер

19

Він називається розширеним синтаксисом виклику. З документації :

Якщо вираз синтаксису * з'являється у виклику функції, вираз повинен перетворитися на послідовність. Елементи з цієї послідовності трактуються так, ніби вони є додатковими позиційними аргументами; якщо є позиційні аргументи x1, ..., xN, і вираз обчислюється у послідовності y1, ..., yM, це еквівалентно виклику з позиційними аргументами M + N x1, ..., xN, y1,. .., yM.

і:

Якщо вираз синтаксису ** з'являється у виклику функції, вираз повинен перетворитися на відображення, вміст якого розглядається як додаткові аргументи ключового слова. У випадку, коли ключове слово відображається як у виразі, так і як явний аргумент ключового слова, виникає виняток TypeError.


3
Просто додавши виноску до відповіді підручника - до того, як надійшла синтаксична підтримка, та ж функціональність була досягнута за допомогою вбудованої apply()функції
Джеремі Браун,

18

У виклику функції одна зірка перетворює список на окремі аргументи (наприклад 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}) ".


3
(дуже) пізній коментар, але я вважаю, що def foo(*x)* x дає кортеж, а не список.
jeremycg
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.