[*a] が過剰割り当てされる原因は何ですか? 質問する

[*a] が過剰割り当てされる原因は何ですか? 質問する

どうやらlist(a)過剰に割り当てていないようだが、[x for x in a]ある時点で過剰に割り当てており、[*a]過剰に割り当てているいつも?

n=100までのサイズ

以下に、0 から 12 までのサイズ n と、3 つの方法の結果のサイズ (バイト単位) を示します。

0 56 56 56
1 64 88 88
2 72 88 96
3 80 88 104
4 88 88 112
5 96 120 120
6 104 120 128
7 112 120 136
8 120 120 152
9 128 184 184
10 136 184 192
11 144 184 200
12 152 184 208

計算すると、repl.it で再現可能Python 3 を使用します。8:

from sys import getsizeof

for n in range(13):
    a = [None] * n
    print(n, getsizeof(list(a)),
             getsizeof([x for x in a]),
             getsizeof([*a]))

それで、これはどのように機能するのでしょうか?overallocateはどのように機能しますか[*a]? 実際には、指定された入力から結果リストを作成するためにどのようなメカニズムが使用されていますか? 反復子を使用してa、 のようなものを使用しますかlist.append? ソース コードはどこにありますか?

データとコードを使ったColab画像を制作した者です。

より小さなnにズームイン:

最大n=40のサイズ

より大きなnにズームアウトします:

n=1000までのサイズ

ベストアンサー1

[*a] 内部的にはCと同等のことをしている:

  1. 新しい空のlist
  2. 電話newlist.extend(a)
  3. 戻り値list

したがって、テストを次のように拡張するとします。

from sys import getsizeof

for n in range(13):
    a = [None] * n
    l = []
    l.extend(a)
    print(n, getsizeof(list(a)),
             getsizeof([x for x in a]),
             getsizeof([*a]),
             getsizeof(l))

オンラインでお試しください!

と の結果は同じであることgetsizeof([*a])がわかります。l = []; l.extend(a); getsizeof(l)

これは通常、正しい方法です。 を実行する場合、extend通常は後でさらに追加することが予想され、同様に、一般的な展開では、複数のものが次々に追加されることが想定されます。 は通常のケースではありません。Python は、複数の項目または反復可能オブジェクトが( )[*a]に追加されると想定するため、一般的なケースでは、過剰割り当てによって作業が節約されます。list[*a, b, c, *d]

対照的に、list単一のサイズ指定済みの反復可能オブジェクト ( を使用list()) から構築された は、使用中に拡大または縮小しない可能性があり、他のことが証明されない限り、過剰割り当ては時期尚早です。Pythonは最近、既知のサイズの入力に対してもコンストラクタが過剰割り当てするバグを修正しました。

内包表記についてはlist、実質的には繰り返しの と同等であるappendため、要素を 1 つずつ追加する場合の通常の過剰割り当て増加パターンの最終結果が表示されます。

誤解のないように言っておきますが、これは言語の保証ではありません。単に CPython がそれを実装する方法です。Python 言語仕様は、一般的に の特定の成長パターンには無関係です(末尾からのおよびlistの償却を保証することを除く)。コメントで述べたように、特定の実装は 3.9 で再び変更されます。 には影響しませんが、以前は「個々の項目の一時的な を構築し、 で」だったものが の複数の適用になり、過剰割り当てがいつ発生するか、どの数値が計算に含まれるかが変わる可能性がある他のケースには影響する可能性があります。O(1) appendpop[*a]tupleextendtupleLIST_APPEND

おすすめ記事