Понимание кортежей Python3.5 действительно ограничено?

Я любил кортежи, добавленные в Python3.5:

In [128]: *(x for x in range(5)),
Out[128]: (0, 1, 2, 3, 4)

Тем не менее, когда я пытаюсь return понимание кортежа напрямую я получаю ошибку:

In [133]: def testFunc():
     ...:     return *(x for x in range(5)),
     ...: 
  File "<ipython-input-133-e6dd0ba638b7>", line 2
    return *(x for x in range(5)),
           ^
SyntaxError: invalid syntax    

Это всего лишь небольшое неудобство, поскольку я могу просто присвоить понимание кортежа переменной и вернуть ее. Однако, если я пытаюсь поместить понимание кортежа в понимание словаря, я получаю ту же ошибку:

In [130]: {idx: *(x for x in range(5)), for idx in range(5)}
  File "<ipython-input-130-3e9a3eee879c>", line 1
    {idx: *(x for x in range(5)), for idx in range(5)}
          ^
SyntaxError: invalid syntax

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

У меня нет проблем с использованием словаря и списков в этих ситуациях. В скольких других ситуациях понимание кортежей не будет работать, когда это делают другие? Или, возможно, я использую это неправильно?

Это заставляет меня задуматься, какой смысл был, если его использование настолько ограничено или, возможно, я делаю что-то не так? Если я не делаю что-то не так, то какой самый быстрый / самый питонный способ создать кортеж, который достаточно универсален, чтобы его можно было использовать так же, как списки и словари?

2 ответа

TLDR: если вы хотите кортеж, передайте выражение генератора tuple:

{idx: tuple(x for x in range(5)) for idx in range(5)}

В Python нет "понимания кортежей". Это:

x for x in range(5)

является генератором выражения. Добавление скобок вокруг него просто используется для отделения его от других элементов. Это так же, как в (a + b) * c, который также не включает в себя кортеж.

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

*[1, 2]                         # illegal - nothing to unpack into
a, b, c, d = *[1, 2], 3, 4      # legal - unpack into assignment tuple

Сейчас занимаюсь *<iterable>, комбинаты * распаковка с собой , кортеж буквальный. Это не применимо во всех ситуациях - разделение элементов может иметь приоритет над созданием кортежа. Например, последний , в [*(1, 2), 3] отделяется, тогда как в [(*(1, 2), 3)] это создает кортеж.

В словаре , является неоднозначным, поскольку он используется для разделения элементов. сравнить {1: 1, 2: 2} и обратите внимание, что {1: 2,3} незаконно Для return Заявление, это может быть возможно в будущем.

Если вы хотите кортеж, вы должны использовать () всякий раз, когда может быть двусмысленность - даже если Python может справиться с этим, иначе трудно разобрать людей.

Когда вашим источником является большой оператор, такой как выражение генератора, я предлагаю явно преобразовать его в кортеж. Сравните следующие две допустимые версии вашего кода для удобства чтения:

{idx: tuple(x for x in range(5)) for idx in range(5)}
{idx: (*(x for x in range(5)),) for idx in range(5)}

Обратите внимание, что списочные и dict-понимания также работают аналогично - они практически аналогичны передаче выражения генератора в list, set или же dict, Они в основном служат, чтобы не смотреть вверх list, set или же dict в глобальном пространстве имен.


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

Под покровом и выражения генератора, и списки / дикты / наборы создают недолговечную функцию. Вы не должны полагаться на понимание для оптимизации производительности, если вы не профилировали и не тестировали их. По умолчанию используйте все, что наиболее читаемо для вашего варианта использования.

dis.dis("""[a for a in (1, 2, 3)]""")
  1           0 LOAD_CONST               0 (<code object <listcomp> at 0x10f730ed0, file "<dis>", line 1>)
              2 LOAD_CONST               1 ('<listcomp>')
              4 MAKE_FUNCTION            0
              6 LOAD_CONST               5 ((1, 2, 3))
              8 GET_ITER
             10 CALL_FUNCTION            1
             12 RETURN_VALUE

Передайте выражение генератора в tuple() конструктор, так как нет кортежей-пониманий:

{idx: tuple(x for x in range(5)) for idx in range(5)}

Понимания кортежей не существуют, но даже при наличии списочных пониманий ([... for ... in ...]) они похожи на *: list(... for ... in ...),


* понимание списка на самом деле быстрее, чем выражение генератора, переданное в функцию конструктора, так как выполнение функций дорого в Python

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