リンクされたリスト、配列ですか?私は周りを検索し、推測している人だけを見つけました。私のCの知識は、ソースコードを見るのに十分ではありません。
動的配列 です。実用的な証拠:インデックス付けは、インデックスに関係なく同じ時間(もちろん、非常に小さな差(0.0013 µsec!)で)かかります。
...>python -m timeit --setup="x = [None]*1000" "x[500]"
10000000 loops, best of 3: 0.0579 usec per loop
...>python -m timeit --setup="x = [None]*1000" "x[0]"
10000000 loops, best of 3: 0.0566 usec per loop
IronPythonまたはJythonがリンクリストを使用している場合、私は驚くでしょう。リストが動的配列であるという仮定に基づいて構築された多くの広く使用されているライブラリのパフォーマンスを台無しにします。
実際、Cコードは非常に単純です。 1つのマクロを展開し、無関係なコメントを削除する基本構造は listobject.h
であり、リストを次のように定義します。
typedef struct {
PyObject_HEAD
Py_ssize_t ob_size;
/* Vector of pointers to list elements. list[0] is ob_item[0], etc. */
PyObject **ob_item;
/* ob_item contains space for 'allocated' elements. The number
* currently in use is ob_size.
* Invariants:
* 0 <= ob_size <= allocated
* len(list) == ob_size
* ob_item == NULL implies ob_size == allocated == 0
*/
Py_ssize_t allocated;
} PyListObject;
PyObject_HEAD
には、参照カウントとタイプ識別子が含まれます。したがって、全体を割り当てるのはベクトル/配列です。そのような配列がいっぱいになったときにサイズを変更するためのコードは listobject.c
にあります。実際には配列を2倍にすることはありませんが、割り当てることで成長します
new_allocated = (newsize >> 3) + (newsize < 9 ? 3 : 6);
new_allocated += newsize;
ここで、newsize
は要求されたサイズです(1つ1つextend
する代わりに任意の数の要素でappend
を使用できるため、必ずしもallocated + 1
ではありません)。
Python FAQ もご覧ください。
CPythonでは、リストはポインターの配列です。 Pythonの他の実装では、さまざまな方法で保存することを選択できます。
これは実装依存ですが、IIRC:
ArrayList
を使用しますしたがって、それらはすべてO(1)ランダムアクセスを持ちます。
ドキュメント によると、
Pythonのリストは、実際には可変長配列であり、LISPスタイルのリンクリストではありません。
Laurent Luceの記事「Pythonリストの実装」をお勧めします 。著者がリストがCPythonでどのように実装されているかを説明し、この目的のために優れた図を使用しているので、私にとって本当に役に立ちました。
オブジェクトCの構造をリストする
CPythonのリストオブジェクトは、次のC構造で表されます。 ob_itemは、リスト要素へのポインタのリストです。 allocateは、メモリに割り当てられたスロットの数です。
typedef struct {
PyObject_VAR_HEAD
PyObject ** ob_item;
Py_ssize_tが割り当てられました。
} PyListObject;
割り当てられたスロットとリストのサイズの違いに注意することが重要です。リストのサイズはlen(l)と同じです。割り当てられたスロットの数は、メモリに割り当てられたものです。多くの場合、割り当てられたサイズはサイズより大きくなることがあります。これは、新しい要素がリストに追加されるたびにreallocを呼び出す必要を避けるためです。
...
追加
リストに整数を追加します:l.append(1)。何が起こるのですか?
さらにもう1つの要素、l.append(2)を追加します。 list_resizeはn + 1 = 2で呼び出されますが、割り当てられたサイズが4であるため、メモリをさらに割り当てる必要はありません。さらに2つの整数を追加すると、同じことが起こります:l.append(3)、l.append(4)。次の図は、これまでの内容を示しています。
...
挿入
...
ポップ
最後の要素l.pop()をポップすると、listpop()が呼び出されます。 list_resizeはlistpop()内で呼び出され、新しいサイズが割り当てられたサイズの半分より小さい場合、リストは縮小されます。
スロット4はまだ整数を指していることがわかりますが、重要なことはリストのサイズが4になったことです。もう1つの要素をポップしましょう。 list_resize()では、size – 1 = 4 – 1 = 3は割り当てられたスロットの半分未満であるため、リストは6スロットに縮小され、リストの新しいサイズは3になりました。
...
他の人が上記で述べたように、リスト(かなり大きい場合)は、固定量のスペースを割り当てることで実装され、そのスペースがいっぱいになったら、より多くのスペースを割り当てて要素をコピーします。
一般性を失うことなく、メソッドがO(1)償却される理由を理解するために、a = 2 ^ n要素を挿入したと仮定し、テーブルを2 ^(n + 1)に倍増する必要がありますサイズ。つまり、現在2 ^(n + 1)の操作を実行しています。最後のコピーでは、2 ^ n回の操作を行いました。その前に、2 ^(n-1)... 8,4,2,1までの処理を行いました。これらを合計すると、1 + 2 + 4 + 8 + ... + 2 ^(n + 1)= 2 ^(n + 2)-1 <4 * 2 ^ n = O(2 ^ n)= O(a)合計挿入(つまりO(1)償却時間)。また、テーブルで削除が許可されている場合、テーブルの縮小は異なる要因(3倍など)で行われる必要があることに注意してください
Pythonのリストは、複数の値を格納できる配列のようなものです。リストは変更可能であるため、変更できます。さらに重要なことは、リストを作成するときに、Pythonがそのリスト変数のreference_idを自動的に作成することです。他の変数を割り当てて変更すると、メインリストが変更されます。例を試してみましょう:
list_one = [1,2,3,4]
my_list = list_one
#my_list: [1,2,3,4]
my_list.append("new")
#my_list: [1,2,3,4,'new']
#list_one: [1,2,3,4,'new']
my_list
を追加しますが、メインリストは変更されています。つまり、そのリストはコピーリストとして割り当てられず、参照として割り当てられます。