3

Python が 1 回だけインスタンス化する整数 (-6 から 256 のようです) を見つけようとしましたが、その過程で、パターンが見えない文字列の動作に出くわしました。 id、時々そうではありません。このコード:

A = "10000"
B = "10000"
C = "100" + "00"
D = "%i"%10000
E = str(10000)
F = str(10000)
G = str(100) + "00"
H = "0".join(("10","00"))

for obj in (A,B,C,D,E,F,G,H):
    print obj, id(obj), obj is A

プリント:

10000 4959776 真
10000 4959776 真
10000 4959776 真
10000 4959776 真
10000 4959456 偽
10000 4959488 偽
10000 4959520 偽
10000 4959680 偽

最初の 4 つには明示的な関数呼び出しがないという事実を除いて、パターンはわかりませんが、+たとえば C の " " はaddへの関数呼び出しを意味するため、そうではありません。特に、C と G が異なる理由がわかりません。これは、追加のコンポーネントの ID が結果よりも重要であることを意味しているためです。

では、AD が受ける特別な処理とは何ですか?

4

4 に答える 4

10

言語仕様に関しては、準拠するPythonコンパイラとランタイムは、不変型のインスタンスに対して、新しいインスタンスを作成するか、必要な値に等しい同じ型の既存のインスタンスを見つけて、新しい参照を使用することが完全に許可されています。その同じインスタンス。これはis、不変要素間の使用またはIDによる比較が常に正しくないことを意味し、マイナーリリースは、最適化を強化するためにこの問題の戦略を微調整または変更する可能性があります。

実装に関しては、トレードオフは非常に明確です。既存のインスタンスを再利用しようとすると、そのようなインスタンスを見つけるのに費やした時間(おそらく無駄になる)が発生する可能性がありますが、試行が成功すると、メモリ(および割り当て時間)がいくらか節約されます。その後、新しいインスタンスを保持するために必要なメモリビットを解放します)。

これらの実装のトレードオフを解決する方法は完全には明らかではありません。適切な既存のインスタンスが見つかる可能性が高く、検索が(失敗した場合でも)高速であることを示すヒューリスティックを特定できる場合は、検索を試行することをお勧めします。 -ヒューリスティックが示唆する場合は再利用しますが、それ以外の場合はスキップします。

あなたの観察では、それが完全に安全で、速く、そして単純であるときに、わずかなのぞき穴最適化を実行する特定のドットリリース実装を見つけたようです。したがって、割り当てAからDはすべてAとまったく同じになります(ただし、EからFは、オプティマイザーの作成者がセマンティクスを想定するのに100%安全ではないと合理的に考えている可能性がある名前付きの関数またはメソッドを含むため、そうしないでください。

したがって、同じインスタンスを再利用するAからDは、AとBに要約されます(CとDは、まったく同じ構成にのぞき穴最適化されるため)。

その再利用は、コンパイラの戦術/オプティマイザのヒューリスティックを明確に示唆しており、同じ関数のローカル名前空間内の不変型の同一のリテラル定数が、関数内の1つのインスタンスのみへの参照に折りたたまれます.func_code.co_consts(関数とコードの属性に現在のCPythonの用語を使用するため)オブジェクト)-1つの関数内で同じ不変定数リテラルを再利用することがいくらか頻繁であり、利点が何度も発生する間(関数が実行されるたびに)、価格は1回だけ(コンパイル時に)支払われるため、合理的な戦術とヒューリスティック多分ループ内など)。

(これらの特定の戦術とヒューリスティックは、明らかに前向きなトレードオフを考えると、最近のすべてのバージョンのCPythonに浸透しており、IronPython、Jython、およびPyPyにも浸透していると思います;-)。

Python自体または同様の言語用のコンパイラ、ランタイム環境、のぞき穴オプティマイザなどを作成することを計画している場合、これはやや価値があり興味深い研究です。内部の深い研究(もちろん、特定のものの癖に固執しないように、多くの異なる正しい実装の理想的です-Pythonは現在、言うまでもなく、少なくとも4つの別々の生産に値する実装を楽しんでいますそれぞれのいくつかのバージョン!)は、間接的に、1つをより優れたPythonプログラマーにするのにも役立ちます-しかし、言語自体によって保証されているものに焦点を当てることは特に重要です。 「ちょうど起こる」部分のため言語仕様によってそうなること)は、いずれかの実装の次のポイントリリースで完全に変更される可能性があり、プロダクションコードが誤ってそのような詳細に依存している場合は、厄介な驚きを引き起こす可能性があります;-)。さらに、言語で義務付けられた動作ではなく、このような変数の実装の詳細に依存する必要はほとんどありません。もちろん、オプティマイザー、デバッガー、プロファイラーなどをコーディングしている場合を除きます。 )。

于 2009-07-19T21:24:36.133 に答える
4

Python は文字列定数をインライン化できます。A、B、C、D は実際には同じリテラルです (Python が定数式を検出すると、それを定数として扱います)。

strは実際にはクラスなので、str(whatever)このクラスのコンストラクターを呼び出して、新しいオブジェクトを生成する必要があります。これにより、E、F、G が説明されます (これらにはそれぞれ別の ID があることに注意してください)。

H についてはよくわかりませんが、この式は複雑すぎて Python が実際には定数であると判断できないため、新しい文字列を計算するという説明が必要です。

于 2009-07-19T20:47:48.873 に答える
1

コンパイル時に評価できる短い文字列は自動的にインターンされると思います。str最後の例では、または再定義される可能性があるため、コンパイル時に結果を評価できませんjoin

于 2009-07-19T20:24:28.330 に答える
1

バイトコードを調べるというS.Lottの提案への答えとして:

import dis
def moo():
    A = "10000"
    B = "10000"
    C = "100" + "00"
    D = "%i"%10000
    E = str(10000)
    F = str(10000)
    G = "1000"+str(0)
    H = "0".join(("10","00"))
    I = str("10000")

    for obj in (A,B,C,D,E,F,G,H, I):
        print obj, id(obj), obj is A
moo()
print dis.dis(moo)

収量:

10000 4968128 True
10000 4968128 True
10000 4968128 True
10000 4968128 True
10000 2840928 False
10000 2840896 False
10000 2840864 False
10000 2840832 False
10000 4968128 True
  4           0 LOAD_CONST               1 ('10000')
              3 STORE_FAST               0 (A)

  5           6 LOAD_CONST               1 ('10000')
              9 STORE_FAST               1 (B)

  6          12 LOAD_CONST              10 ('10000')
             15 STORE_FAST               2 (C)

  7          18 LOAD_CONST              11 ('10000')
             21 STORE_FAST               3 (D)

  8          24 LOAD_GLOBAL              0 (str)
             27 LOAD_CONST               5 (10000)
             30 CALL_FUNCTION            1
             33 STORE_FAST               4 (E)

  9          36 LOAD_GLOBAL              0 (str)
             39 LOAD_CONST               5 (10000)
             42 CALL_FUNCTION            1
             45 STORE_FAST               5 (F)

 10          48 LOAD_CONST               6 ('1000')
             51 LOAD_GLOBAL              0 (str)
             54 LOAD_CONST               7 (0)
             57 CALL_FUNCTION            1
             60 BINARY_ADD          
             61 STORE_FAST               6 (G)

 11          64 LOAD_CONST               8 ('0')
             67 LOAD_ATTR                1 (join)
             70 LOAD_CONST              12 (('10', '00'))
             73 CALL_FUNCTION            1
             76 STORE_FAST               7 (H)

 12          79 LOAD_GLOBAL              0 (str)
             82 LOAD_CONST               1 ('10000')
             85 CALL_FUNCTION            1
             88 STORE_FAST               8 (I)

 14          91 SETUP_LOOP              66 (to 160)
             94 LOAD_FAST                0 (A)
             97 LOAD_FAST                1 (B)
            100 LOAD_FAST                2 (C)
            103 LOAD_FAST                3 (D)
            106 LOAD_FAST                4 (E)
            109 LOAD_FAST                5 (F)
            112 LOAD_FAST                6 (G)
            115 LOAD_FAST                7 (H)
            118 LOAD_FAST                8 (I)
            121 BUILD_TUPLE              9
            124 GET_ITER            
        >>  125 FOR_ITER                31 (to 159)
            128 STORE_FAST               9 (obj)

 15         131 LOAD_FAST                9 (obj)
            134 PRINT_ITEM          
            135 LOAD_GLOBAL              2 (id)
            138 LOAD_FAST                9 (obj)
            141 CALL_FUNCTION            1
            144 PRINT_ITEM          
            145 LOAD_FAST                9 (obj)
            148 LOAD_FAST                0 (A)
            151 COMPARE_OP               8 (is)
            154 PRINT_ITEM          
            155 PRINT_NEWLINE       
            156 JUMP_ABSOLUTE          125
        >>  159 POP_BLOCK           
        >>  160 LOAD_CONST               0 (None)
            163 RETURN_VALUE        

実際、コンパイラはADが同じことを意味することを理解しているように見えるため、一度だけ生成することでメモリを節約します(Alex、Maciej、およびGregが示唆しているように)。(追加されたケースIは、文字列から文字列を作成しようとしていることに気づき、それを渡すだけの str() のようです。)

皆さん、ありがとうございます。これでより明確になりました。

于 2009-07-19T23:42:25.730 に答える