Розпакування кортежу Python в операторі return


76

Мова Python (особливо 3.x) дозволяє дуже загальне розпаковування ітерацій, простим прикладом яких є

a, *rest = 1, 2, 3

Протягом багатьох років це розпакування поступово узагальнювалось (див., Наприклад, PEP 3132 та PEP 448 ), дозволяючи використовувати його у все більшій кількості обставин. Тому я був здивований, виявивши, що наступне є неприпустимим синтаксисом у Python 3.6 (і залишається таким у Python 3.7):

def f():
    rest = [2, 3]
    return 1, *rest  # Invalid

Я можу змусити це працювати, інкапсулюючи повернутий кортеж у дужки так:

def f():
    rest = [2, 3]
    return (1, *rest)  # Valid

Той факт, що я використовую це у returnзаяві, здається важливим, як

t = 1, *rest

насправді є законним і призводить до того ж, як з дужками, так і без них.

Чи цей випадок просто забутий розробниками Python, чи є якісь причини, чому цей випадок є недійсним синтаксисом?

Чому я дбаю

Це порушує важливий контракт, який, на мою думку, я мав з мовою Python. Розглянемо наступне (також дійсне) рішення:

def f():
    rest = [2, 3]
    t = 1, *rest
    return t

Зазвичай, коли у мене є такий код, я вважаю tтимчасове ім'я, від якого я мав би змогу позбутися, просто замінивши його tв нижньому рядку його визначенням. Однак у цьому випадку це призводить до недійсного коду

def f():
    rest = [2, 3]
    return 1, *rest

Звичайно, немає особливої ​​праці розміщувати дужки навколо поверненого значення, але зазвичай додаткові дужки потрібні лише для того, щоб розрізнити кілька можливих результатів (групування). Тут це не так, оскільки залишення дужок не спричиняє якоїсь іншої небажаної поведінки, а навпаки, взагалі ніякої поведінки.

Оновлення

Оскільки Python 3.8 (див. Пункт 7 у цьому списку ), узагальнений синтаксис, обговорений вище, тепер діє.


4
Це насправді більше наслідок синтаксису граматики, ніж будь-що інше.
cs95

Ви також не можете просто повернути * rest, це неприпустимий синтаксис.
lapisdecor

3
@lapisdecor Так, але це узгоджується з фактом, який t = *restє недійсним. Крім того, return *restі t = *restне представляє фактичного розпакування, тому я не вважаю проблемою, що це не дозволено. Якби це було дозволено, само *restпо собі це було б лише заплутаним синтаксисом для tuple(rest).
jmd_dk

Це трапляється не просто return. Розпакування також заборонено в yieldаргументі, нижньому індексі, RHS доповненого нижньому індексі присвоєння (але не звичайного присвоєння) та праворуч inу forвиписці, незважаючи на те, що невідомі кортежі допускаються у всіх цих позиціях, оскільки синтаксис для цих речі використовує expression_listзамість starred_expression.
user2357112 підтримує Моніку

3
Зверніть увагу на різницю між t = *restі t = *rest,. Останнє є дійсним.
senderle

Відповіді:


38

Я підозрюю, що це випадковість, виходячи з коментарів цього комітету щодо Python 3.2.

Цей коміт дозволив виразу присвоєння взяти testlist_star_exprвиробництво (що дозволяє нерозмірному розпаковуванню), але залишив оператор return, який бере testlistвиробництво. Я підозрюю, що коміт щойно пропустив це (і, можливо, інші місця, але я зосереджуюсь наreturn_stmt наразі виробництві).

Я продовжив і модифікував файл Python Grammar / Grammar, щоб дозволити це. Всі тести продовжують проходити, включаючи тести вtest_grammar.py файлі (але це не здається надзвичайно вичерпним).

Якщо вам цікаво, це зміни я зробив . Не соромтеся клонувати або завантажувати мою вилку .

ОНОВЛЕННЯ: Я подав випуск bpo та запит на повернення (та прибутковість) розпакування.


3
Ви вже робили запит на витяг із цією зміною?
Мартін Пітерс

Ще ні. Хотіли подивитися, чи правильно це вирішує те, що було після @jmd_dk, і, можливо, поглянути на кілька інших випадків (наприклад, yield_stmtвиробництво), перш ніж надсилати його.
Девід Катберт,

2
Створення "виправлення" здається трохи дозрілим, можливо, спочатку надішліть звіт про помилку, якщо вважаєте, що це аварія bugs.python.org
Chris_Rands

3
Виправлення з’явиться у Python 3.8
Майкл Скотт Катберт,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.