2012-11-21 11 views
7

リストがあります:nodes = [20、21、22、23、24、25]。Pythonで生成するときの "()"と "[]"の違いは何ですか?

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

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

私がしたい:tour2がリストされている間

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

tour1のタイプは発電機である:

は、私は、新しい2次元オブジェクトを生成するために2つの方法を使用しましたなぜ tour1はタプルではないのか知っていますか?ありがとう。

+6

これは発電機なので、あなたの質問は何ですか? –

+3

@ダニエルQuestionmarkの前の部分。なぜタプルはありませんか? – RickyA

答えて

10

基本的な違いは、最初のものがジェネレータ式であり、2番目のものがリスト内包であることです。前者は、必要なときだけ要素を生成しますが、後者は、理解を実行するときに常にリスト全体を生成します。詳細情報については

、あなたが最初の構文から期待しているように見えるものですPythonで「タプル理解」、のようなものはありませんGenerator Expressions vs. List Comprehension

を参照してください。

あなたはタプルのタプルに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))) 
+0

Generator Expressions vs. List Comprehensionに関する質問を読んだことがあります。私は、ノード内のbの[[a(a、b)in a nodes]よりも効率的であるという表現を 'list(tuple(a in b)for a nodes for a nodes) ] '? – zfz

10

タプルの構文はかっこではなく、()です。コンマ,です。あなたは括弧なしでタプルを作成することができます。

x = 1, 2, 3 

あなたが理解からタプルを作成したい場合は、単にtupleコンストラクタを使用します。それは発電機ですが、あなたは、単にタプルに変更することができ

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

ありがとうございます。私はタプルの使い方を誤解していました。それから、次のように生成します:ノード内のbについて、tour1 = tuple((inのノードの場合は((a、b)))。ツアー1はタプルです! – zfz

2

を:

>>> (i for i in xrange(4)) 
<generator object <genexpr> at 0x23ea9b0> 
>>> tuple(i for i in xrange(4)) 
(0, 1, 2, 3) 
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標準モジュールがあります。のは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コンパイラは、より簡単なコードを生成することができます。

+0

ありがとうございます。私はモジュール 'dis'に精通していません。だから私は 'fn1'は 'fn2'よりも効率的だと思いますか? – zfz

+0

@zfz:いいえ、それはコードが異なっていることだけを示しています。実際には、 'fn1'コードは、ジェネレータの機能を呼び出してその結果を取得するためのラッパーです。 'fn2'はよりインラインで、より直接的です。私は私の答えを更新するつもりです。 – pepr

関連する問題