10

私の質問にかなり似ているthisthisを読んだ後、私はまだ次の動作を理解できません:

a = 257
b = 257
print(a is b) #False
a, b = 257, 257
print(a is b) #True

印刷するid(a)id(b)、値が別々の行に割り当てられた変数のIDが異なることがわかりますが、複数の割り当てでは両方の値が同じIDを持っています:

a = 257
b = 257
print(id(a)) #139828809414512
print(id(b)) #139828809414224
a, b = 257, 257
print(id(a)) #139828809414416
print(id(b)) #139828809414416

しかし、同じ値を複数代入すると常に同じ ID へのポインターが作成されると言って、この動作を説明することは不可能です。

a, b = -1000, -1000  
print(id(a)) #139828809414448
print(id(b)) #139828809414288

変数が同じになるときとそうでないときを説明する明確なルールはありますidか?

編集

関連情報:この質問のコードはインタラクティブモード(ipython3)で実行されました

4

3 に答える 3

3

UNPACK_SEQUENCEこれは、定数値をロードする際の pythons インタープリターの最適化によるものです。アンパック中に Python が iterable に遭遇した場合、重複オブジェクトを複数回ロードするのではなく、最初のオブジェクトを保持し、すべての重複変数名を 1 つのポインターに割り当てます (CPython 実装)。したがって、すべての変数は 1 つのオブジェクトへの同じ参照になります。Python レベルでは、この動作は、重複キーを保持しない名前空間として辞書を使用していると考えることができます。

つまり、解凍は次のコマンドと同等です。

a = b = 257

負の数については、python 2.X では違いはありませんが、python 3.X では、-5 より小さい数値の場合、python はアンパック中に新しいオブジェクトを作成するようです:

>>> a, b = -6, -6
>>> a is b
False
>>> a, b = -5, -5
>>> 
>>> a is b
True
于 2016-02-08T17:12:18.273 に答える
2

これは、バイトコード コンパイラでのコンスタント フォールディングの最適化によるものです。バイトコード コンパイラがステートメントのバッチをコンパイルするとき、dict を使用して、検出された定数を追跡します。この dict は、同等の定数を自動的にマージします。

定数の記録と番号付けを担当するルーチンを次に示します (およびいくつかの関連する責任):

static int
compiler_add_o(struct compiler *c, PyObject *dict, PyObject *o)
{
    PyObject *t, *v;
    Py_ssize_t arg;

    t = _PyCode_ConstantKey(o);
    if (t == NULL)
        return -1;

    v = PyDict_GetItem(dict, t);
    if (!v) {
        arg = PyDict_Size(dict);
        v = PyInt_FromLong(arg);
        if (!v) {
            Py_DECREF(t);
            return -1;
        }
        if (PyDict_SetItem(dict, t, v) < 0) {
            Py_DECREF(t);
            Py_DECREF(v);
            return -1;
        }
        Py_DECREF(v);
    }
    else
        arg = PyInt_AsLong(v);
    Py_DECREF(t);
    return arg;
}

同等の定数が既に存在しない場合にのみ、新しいエントリを追加して新しい番号を割り当てることがわかります。(この_PyCode_ConstantKeyビットは0.0、 、-0.0、および0が同等でないと見なされることを確認します。)

対話モードでは、インタープリターが実際にコマンドを実行する必要があるたびにバッチが終了するため、コマンド全体で定数の折りたたみはほとんど発生しません。

>>> a = 1000
>>> b = 1000
>>> a is b
False
>>> a = 1000; b = 1000 # 1 batch
>>> a is b
True

スクリプトでは、すべての最上位ステートメントが 1 つのバッチであるため、より多くの定数の折りたたみが発生します。

a = 257
b = 257
print a is b

スクリプトでは、これはTrue.

関数のコードは、関数の外側のコードとは別に追跡される定数を取得します。これにより、定数の折りたたみが制限されます。

a = 257

def f():
    b = 257
    print a is b

f()

スクリプトでも、これは出力しFalseます。

于 2016-02-08T17:53:04.487 に答える
0

そのようなルールは実装固有です。たとえば、CPython はint、パフォーマンスの最適化として、小さい整数 (-5 から 256) のオブジェクトを事前に割り当てます。

唯一の一般的なルールは、リテラルを使用すると新しいオブジェクトが生成されると想定することです。

于 2016-02-08T17:10:48.197 に答える