Понимание кортежей 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