2012-01-11 14 views
13
>>> rows = [['']*5]*5 
>>> rows 
[['', '', '', '', ''], ['', '', '', '', ''], ['', '', '', '', ''], ['', '', '', '', ''], ['', '', '', '', '']] 
>>> rows[0][0] = 'x' 

は当然のことながら、私は、行になることを期待:リスト上の乗算演算子を使用してポインタのリストを作成するのはなぜですか?

[['x', '', '', '', ''], ['', '', '', '', ''], ['', '', '', '', ''], ['', '', '', '', ''], ['', '', '', '', '']] 

その代わり、私が手:

[['x', '', '', '', ''], ['x', '', '', '', ''], ['x', '', '', '', ''], ['x', '', '', '', ''], ['x', '', '', '', '']] 

*行リストの要素は[ '']同じ古いへのポインタであると思われます5リスト。なぜこのように動作し、これはPythonの機能ですか?

+0

リストからリストの理解構文を作成した場合、「正しく動作しています」というメッセージが表示されます。 範囲(5)のyに対して 'rows = [['は範囲(5) ] ' – xyzman

+0

これは"動作 "します:' rows = [[''] * 5(範囲(5)のyに対して) ' – xyzman

答えて

15

動作は反復演算子(*)に固有のものではありません。あなたが+を使用して2つのリストを連結する場合、例えば、動作は同じです:

In [1]: a = [[1]] 

In [2]: b = a + a 

In [3]: b 
Out[3]: [[1], [1]] 

In [4]: b[0][0] = 10 

In [5]: b 
Out[5]: [[10], [10]] 

これは、リストがオブジェクトであり、オブジェクトが参照することにより保存されているという事実に関係しています。 *などを使用すると、それは繰り返される参照、したがってあなたが見ている動作です。

以下はrowsのすべての要素が同じアイデンティティ(CPythonの中すなわちメモリアドレス)を持っていることを示しています

In [6]: rows = [['']*5]*5 

In [7]: for row in rows: 
    ...:  print id(row) 
    ...:  
    ...:  
15975992 
15975992 
15975992 
15975992 
15975992 

それは、行のための5つの別個のリスト作成を除き、次はあなたの例と同等です:

rows = [['']*5 for i in range(5)] 
3

名前、関数パラメータ、およびコンテナが参照セマンティクスを持つという事実は、Pythonの非常に基本的な設計決定です。これは、Pythonが多くの面でどのように動作するかに影響し、これらの側面のうちの1つだけを選択したことになります。多くの場合、参照セマンティクスはより便利ですが、他の場合はコピーがより便利になります。必要に応じて、Pythonでは、あなたは常に明示的にコピーを作成することができ、または、この場合には、代わりにリストの内包表記を使用します。

rows = [[''] * 5 for i in range(5)] 

をあなたは異なる意味を持つプログラミング言語を設計することができ、そして持っていない多くの言語があります異なるセマンティクス、同様のセマンティクスを持つ言語が含まれます。なぜこの決定がなされたのかは少し難解です。言語にはいくつかのセマンティクスがあるだけで、なぜあなたはいつもその理由を尋ねることができます。あなたはPythonが動的に型付けされている理由を尋ねることができます。答えは、これがちょうどGuidoが1989年に決めた方法です。

3

あなたはPythonが "under the hood"これは機能です。私は彼らがなぜこのようにしたのか分かりません。スピードとメモリー使用量を減らすためだと思います。

shallow copies and deep copiesの区別を理解することが重要なのはなぜですか。

関連する問題