Python3.5元组理解真的如此有限吗?
我一直喜欢Python3.5中添加的元组理解:Python3.5元组理解真的如此有限吗?,python,python-3.x,tuples,Python,Python 3.x,Tuples,我一直喜欢Python3.5中添加的元组理解: In [128]: *(x for x in range(5)), Out[128]: (0, 1, 2, 3, 4) 然而,当我试图直接返回元组理解时,我得到一个错误: In [133]: def testFunc(): ...: return *(x for x in range(5)), ...: File "<ipython-input-133-e6dd0ba638b7>", line 2
In [128]: *(x for x in range(5)),
Out[128]: (0, 1, 2, 3, 4)
然而,当我试图直接返回元组理解时,我得到一个错误:
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
[133]中的:def testFunc():
…:返回*(范围(5)中的x代表x),
...:
文件“”,第2行
返回*(范围(5)内的x对应x),
^
SyntaxError:无效语法
这只是一个小小的不便,因为我可以简单地将元组理解分配给变量并返回变量。但是,如果我尝试将元组理解放在字典理解中,我会得到相同的错误:
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
In[130]:{idx:*(x表示范围(5)中的x),表示范围(5)中的idx}
文件“”,第1行
{idx:*(x表示范围(5)中的x),表示范围(5)中的idx}
^
SyntaxError:无效语法
我觉得这是一个更大的问题,因为压缩在某些情况下对性能很重要
在这种情况下,我使用字典和列表理解没有问题。还有多少情况是元组理解在其他情况下不起作用?或者我用错了
这让我想知道,如果它的用途如此有限,或者我做错了什么,那重点是什么?如果我没有做错什么,那么创建元组的最快/最具python风格的方法是什么,该元组的版本足以与列表和字典理解相同?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中的相同,也不涉及元组
*
符号用于迭代器打包/解包。生成器表达式恰好是iterable,因此可以将其解包。但是,必须有一些东西可以打开iterable。例如,还可以将列表解包到分配的元素中:
*[1, 2] # illegal - nothing to unpack into
a, b, c, d = *[1, 2], 3, 4 # legal - unpack into assignment tuple
现在,执行*,
将*
解包与,
元组文字相结合。这并不是在所有情况下都可以使用的,但是-分隔元素可能优先于创建元组。例如,在[*(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
我觉得这是一个更大的问题,因为压缩在某些情况下对性能很重要
在封面下,生成器表达式和list/dict/set理解都创建了一个短期函数。您不应该依赖于理解来优化性能,除非您已经对其进行了分析和测试。默认情况下,使用对您的用例最具可读性的内容
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
dis.dis(“[a代表(1,2,3)]”)
1 0加载常数0(<0x10f730ed0处的代码对象列表,文件“dis”,第1行>)
2荷载常数1(“”)
4生成函数0
6荷载常数5((1、2、3))
8获得
10调用函数1
12返回值
将生成器表达式传递到tuple()构造函数中,因为:
元组理解不存在,但即使列表理解存在([…for…in…][/code>),它们也类似于*:列表(…for…in…
)
*列表理解实际上比传递到构造函数中的生成器表达式要快,因为在Python中执行函数很昂贵这不是元组理解,而是生成器表达式。*
不是生成器表达式的一部分!它是解包的符号。您可以添加括号:return(*(…),)
,但您更应该使用tuple(…)
。明确考虑并拒绝在理解中使用星号解包:有关详细信息,请参阅。然而,return
中的无效语法让我吃惊;有可能这只是一个疏忽。“返回语句和字典值都不适用于此。”@jpp是的,反汇编显示了MAKE\u函数
。列表理解会消耗中间生成器,因此您永远无法访问它。@jpp正确的是,它不是理解的生成器函数,而是函数。抽象是有漏洞的,函数可以转换为生成器函数:注意,我并不是说列表理解比生成器表达式+列表慢。不过,它们可能比其他方法慢。使用a=[];a、 extend(map(func,range(10000))
在我的测试中比[func(i)for i in range(10000)]
快。@MisterMiyagi:是的,但是我看不出有什么好的理由使返回*(1,2),3
非法,这对x=*(1,2),3
(这是合法的)。返回的中没有歧义,至少
{idx: tuple(x for x in range(5)) for idx in range(5)}