7

有一个列表: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不是元组?谢谢。

4

5 回答 5

10

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

x = 1, 2, 3

如果要从推导式创建元组,只需使用tuple构造函数:

tuple(tuple((a,b) for a in nodes )for b in nodes)
于 2012-11-21T10:51:53.213 回答
10

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

有关详细信息,请参阅生成器表达式与列表理解

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)))
于 2012-11-21T10:53:08.523 回答
5

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

于 2012-11-21T10:53:37.353 回答
2

它是生成器,但您可以简单地将其更改为元组:

>>> (i for i in xrange(4))
<generator object <genexpr> at 0x23ea9b0>
>>> tuple(i for i in xrange(4))
(0, 1, 2, 3)
于 2012-11-21T10:52:05.407 回答
2

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

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

它产生的结果与您的tour2. 这样,您可以将[as 视为 的语法糖list(,而 the]是与 . 相关的语法糖)。但是,编译器对它的编译方式不同。您可以尝试反汇编(您需要传递一个函数):

>>> 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它更快(具有更短的代码)。但是,在所有语言中的所有函数调用的情况下,函数调用看起来都比展开的代码短。它没有说明被调用代码的内部结构。Python之禅的几点:

>>> 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 编译器可以生成更直接的代码。

于 2012-11-21T11:36:27.360 回答