在Python中生成时,“()”和“[]”之间有什么区别?

时间:2012-11-21 10:49:34

标签: python list tuples generator

有一个列表:nodes = [20,21,22,23,24,25]。

我使用两种方法来生成新的二维对象:

tour1 = (((a,b) for a in nodes )for b in nodes)
tour2 = [[(a,b) for a in nodes ]for b in nodes]

tour1 的类型是一个生成器,而 tour2 是一个列表:

In [34]: type(tour1)
Out[34]: <type 'generator'>

In [35]: type(tour2)
Out[35]: <type 'list'>

我想知道为什么 tour1 不是元组?感谢。

5 个答案:

答案 0 :(得分:10)

元组的语法不是括号(),而是逗号,。您可以创建一个没有括号的元组:

x = 1, 2, 3

如果你想从理解中创建一个元组,只需使用tuple构造函数:

tuple(tuple((a,b) for a in nodes )for b in nodes)

答案 1 :(得分:10)

根本区别在于第一个是生成器表达式,第二个是列表推导。前者仅在需要时产生元素,而后者在运行理解时总是产生整个列表。

有关详细信息,请参阅Generator Expressions vs. List Comprehension

在Python中没有“元组理解”这样的东西,这是你似乎期望从第一个语法。

如果您希望将tour1转换为元组元组,可以使用以下内容:

In [89]: tour1 = tuple(tuple((a,b) for a in nodes )for b in nodes)

In [90]: tour1
Out[90]: 
(((20, 20), (21, 20), (22, 20), (23, 20), (24, 20), (25, 20)),
 ((20, 21), (21, 21), (22, 21), (23, 21), (24, 21), (25, 21)),
 ((20, 22), (21, 22), (22, 22), (23, 22), (24, 22), (25, 22)),
 ((20, 23), (21, 23), (22, 23), (23, 23), (24, 23), (25, 23)),
 ((20, 24), (21, 24), (22, 24), (23, 24), (24, 24), (25, 24)),
 ((20, 25), (21, 25), (22, 25), (23, 25), (24, 25), (25, 25)))

答案 2 :(得分:5)

因为语法(x for x in l)是所谓的“生成器表达式”:请参阅http://docs.python.org/2/reference/expressions.html#generator-expressions

答案 3 :(得分:2)

它是生成器,但您只需将其更改为元组:

>>> (i for i in xrange(4))
<generator object <genexpr> at 0x23ea9b0>
>>> tuple(i for i in xrange(4))
(0, 1, 2, 3)

答案 4 :(得分:2)

要添加......实际上,生成器表达式根本不需要括号。当生成器表达式生成错误的语法时,您只需要它们 - 这是因为赋值。将生成器传递给函数(或类似函数)时,不需要括号。请尝试以下方法:

tour3 = list(list((a,b) for a in nodes) for b in nodes)

它产生与tour2完全相同的结果。这样,您可以查看[作为list(的句法糖,]是与)相关的句法糖。但是,编译器对它进行了不同的编译。您可以尝试反汇编(您需要传递一个函数):

>>> import dis
>>> def fn1():
...   return list(list((a,b) for a in nodes) for b in nodes)
...
>>> def fn2():
...   return [[(a,b) for a in nodes ]for b in nodes]
...
>>> dis.dis(fn1)
  2           0 LOAD_GLOBAL              0 (list)
              3 LOAD_CONST               1 (<code object <genexpr> at 000000000229A9B0, file "<stdin>", line 2>)
              6 MAKE_FUNCTION            0
              9 LOAD_GLOBAL              1 (nodes)
             12 GET_ITER
             13 CALL_FUNCTION            1
             16 CALL_FUNCTION            1
             19 RETURN_VALUE
>>> dis.dis(fn2)
  2           0 BUILD_LIST               0
              3 LOAD_GLOBAL              0 (nodes)
              6 GET_ITER
        >>    7 FOR_ITER                37 (to 47)
             10 STORE_FAST               0 (b)
             13 BUILD_LIST               0
             16 LOAD_GLOBAL              0 (nodes)
             19 GET_ITER
        >>   20 FOR_ITER                18 (to 41)
             23 STORE_FAST               1 (a)
             26 LOAD_FAST                1 (a)
             29 LOAD_FAST                0 (b)
             32 BUILD_TUPLE              2
             35 LIST_APPEND              2
             38 JUMP_ABSOLUTE           20
        >>   41 LIST_APPEND              2
             44 JUMP_ABSOLUTE            7
        >>   47 RETURN_VALUE

所以你可以看到它是不同的(即看起来像语法糖,但事实并非如此)。不幸的是,Python不知道如何反汇编生成器:

>>> g = (list((a,b) for a in nodes) for b in nodes)
>>> dis.dis(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Python27\lib\dis.py", line 49, in dis
type(x).__name__
TypeError: don't know how to disassemble generator objects

<强>更新

在查看上面的反汇编代码时,可能会感到诱惑 - fn1更快(代码更短)。但是,所有语言中的所有函数调用都是函数调用看起来比展开代码短的情况。它没有说明被调用代码的内部。禅宗的一些要点:

>>> import this
The Zen of Python, by Tim Peters
...
Readability counts.
...
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
...
>>>

timeit标准模块来衡量执行时间。让我们尝试在两种情况下使用它:

>>> import timeit
>>> t = timeit.Timer('list(list((a,b) for a in nodes) for b in nodes)',
...                  'nodes = [20, 21, 22, 23, 24, 25]')
>>> print("%.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000))
17.74 usec/pass

现在使用方括号:

>>> t = timeit.Timer('[[(a,b) for a in nodes ]for b in nodes]',
...                  'nodes = [20, 21, 22, 23, 24, 25]')
>>> print("%.2f usec/pass" % (1000000 * t.timeit(number=100000)/100000))
7.14 usec/pass
>>>

这清楚地表明,通过[ ]制作列表列表的速度更快。原因是函数调用较少。 Python编译器可以生成更直接的代码。