id()
Python サブインタープリターの初期化 (Python/C API から) と Python関数の内部動作について質問があります。より正確には、WSGI Python コンテナー (Apache の nginx および mod_wsgi で使用される uWSGI など) でのグローバル モジュール オブジェクトの処理についてです。
次のコードは、上記の両方の環境で期待どおりに (分離して) 動作しますが、関数が実行されるプロセス/サブインタープリターに関係なく、関数が常に変数ごとに同じ値を返す理由id()
を自分で説明することはできません。
from __future__ import print_function
import os, sys
def log(*msg):
print(">>>", *msg, file=sys.stderr)
class A:
def __init__(self, x):
self.x = x
def __str__(self):
return self.x
def set(self, x):
self.x = x
a = A("one")
log("class instantiated.")
def application(environ, start_response):
output = "pid = %d\n" % os.getpid()
output += "id(A) = %d\n" % id(A)
output += "id(a) = %d\n" % id(a)
output += "str(a) = %s\n\n" % a
a.set("two")
status = "200 OK"
response_headers = [
('Content-type', 'text/plain'), ('Content-Length', str(len(output)))
]
start_response(status, response_headers)
return [output]
1 つのマスター プロセスと 2 つのワーカーを使用して、uWSGI でこのコードをテストしました。mod_wsgi では、プロセスごとに 2 つのプロセスと 1 つのスレッドを持つデーモン モードを使用します。一般的な出力は次のとおりです。
pid = 15278
id(A) = 139748093678128
id(a) = 139748093962360
str(a) = 1
最初のロード時、次に:
pid = 15282 id
(A) = 139748093678128
id(a) = 139748093962360
str(a) = 1
2番目に、次に
ピッド = 15278 | pid = 15282 id
(A) = 139748093678128
id(a) = 139748093962360
str(a) = 2
お互いに。ご覧のとおりid()
、クラスとクラス インスタンスの両方の (メモリの場所) は、両方のプロセス (上記の最初/2 番目のロード) で同じままですが、同時にクラス インスタンスは別のコンテキストに存在します (そうしないと、2 番目の要求が表示されます)。 「1」ではなく「2」)!
答えはPythonのドキュメントにヒントがあると思います:
id(object)
:オブジェクトの「アイデンティティ」を返します。これは、このオブジェクトの有効期間中に一意で一定であることが保証されている整数 (または長整数) です。有効期間が重複しない 2 つのオブジェクトは、同じ
id()
値を持つ場合があります。
しかし、それが本当にその理由である場合、id()
値がオブジェクトのアドレスであると主張する次のステートメントに悩まされています!
これが、サードパーティの拡張モジュールでオブジェクト参照 (ポインタ) をキャッシュする問題を解決 (またはむしろ修正)する Python/C API の「巧妙な」機能である可能性が非常に高いという事実を高く評価していますが、それでもこの動作には一貫性がないことがわかります。と... まあ、常識。誰かがこれを説明してもらえますか?
また、mod_wsgi は各プロセスでモジュールをインポートする (つまり2 回) ことに気付きましたが、uWSGI は両方のプロセスでモジュールを1回だけインポートします。uWSGI マスター プロセスがインポートを行うため、そのコンテキストのコピーを子プロセスにシードすると思います。その後、両方のワーカーが独立して動作し (ディープ コピー?)、同時に同じオブジェクト アドレスを使用しているように見えます。(また、ワーカーはリロード時に元のコンテキストに再初期化されます。)
このような長い投稿で申し訳ありませんが、十分な詳細を提供したかったのです。ありがとうございました!