7

リストがあります: ノード = [20, 21, 22, 23, 24, 25]。

新しい 2 次元オブジェクトを生成するために、次の 2 つの方法を使用しました。

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

基本的な違いは、1 つ目はジェネレータ式で、2 つ目はリスト内包表記であることです。前者は必要な要素のみを生成しますが、後者は内包表記の実行時に常にリスト全体を生成します。

詳細については、「ジェネレーター式とリスト内包表記」を参照してください。

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(]は に関連するシンタックス シュガー)です。ただし、コンパイラによるコンパイル方法は異なります。逆アセンブルを試みることができます (関数を渡す必要があります):

>>> 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(コードが短いほど) 高速であると思われるかもしれません。ただし、関数呼び出しが展開されたコードよりも短く見えるのは、すべての言語のすべての関数呼び出しの場合です。呼び出されたコードの内部については何も言いません。The Zen of 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実行時間を測定する標準モジュールがあります。次の 2 つの場合に使用してみましょう。

>>> 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 に答える