CPythonはこのトピックに関するObjects / typeobject.cにコメントがあります。
3.5より前のバージョンのCPythonでは、compatible_for_assignmentHEAPTYPE以外のクラスのメモリレイアウトやスロットなどの互換性を正しくチェックするようにのコード
が設定されていなかったため、__class__HEAPTYPE-> HEAPTYPE以外の場合は割り当てを許可しませんでした。
3.5開発サイクル中に、compatible_for_assignment任意のタイプ間の互換性を正しくチェックするようにコードを修正し
__class__、古いタイプと新しいタイプが実際に互換性のあるスロットとメモリレイアウトを持っているすべてのケースで割り当てを許可し始めました(それらがHEAPTYPEとして実装されているかどうかに関係なく)か否か)。
しかし、3.5がリリースされる直前に、インタプリタが不変であると想定し、いくつかの値をインターンするというintのような不変の型で問題が発生することを発見しました。以前はこれは問題ではありませんでした。それらは本当に不変だったためです。特に、インタープリターがこのインターントリックを適用したすべてのタイプは静的に割り当てられていたため、古いHEAPTYPEルールによって「誤って」__class__割り当てが許可されていませんでした。しかし、__class__割り当ての変更により、次のようなコードを許可し始めました
class MyInt(int):
# ...
# Modifies the type of *all* instances of 1 in the whole program,
# including future instances (!), because the 1 object is interned.
(1).__class__ = MyInt
(https://bugs.python.org/issue24912を参照してください)。
理論的には、適切な修正は、どのクラスがこの不変式に依存しているかを特定__class__し、おそらく新しいPy_TPFLAGS_IMMUTABLEフラグ(「ブラックリスト」アプローチ)などのメカニズムを介して、それらだけに割り当てを許可しないことです。しかし実際には、この問題は3.5 RCサイクルの後半には気付かなかったため、保守的なアプローチを採用し、以前と同じHEAPTYPE-> HEAPTYPEチェックと「ホワイトリスト」を復活させています。今のところ、ホワイトリストはModuleTypeサブタイプのみで構成されています。これは、そもそもパッチの動機となったケースであるためです。https://bugs.python.org/issue22986を参照してください。モジュールオブジェクトは変更可能であるため、確実に確認できます。彼らは絶対に抑留されていないこと。したがって、HEAPTYPE-> HEAPTYPE または
ModuleTypeサブタイプ-> ModuleTypeサブタイプ。
私たちが知る限り、次の 'if'ステートメントを超えるすべてのコードは非HEAPTYPEクラスを正しく処理し、HEAPTYPEチェックは、インタープリターが以下の前提でベイクした非HEAPTYPEクラスのサブセットを保護するためにのみ必要ですすべてのインスタンスは真に不変です。
説明:
CPythonは2つの方法でオブジェクトを保存します。
オブジェクトは、ヒープに割り当てられた構造です。オブジェクトが適切にガベージコレクションされるようにするため、オブジェクトの使用には特別なルールが適用されます。オブジェクトが静的またはスタックに割り当てられることはありません。特別なマクロと関数を介してのみアクセスする必要があります。 (タイプオブジェクトは最初のルールの例外です。標準タイプは静的に初期化されたタイプオブジェクトで表されますが、Python 2.2のタイプ/クラス統合での作業により、ヒープに割り当てられたタイプオブジェクトも使用できるようになりました)。
Include / object.hのコメントからの情報。
新しい値をに設定しようとするsome_obj.__class__と、object_set_class関数が呼び出されます。PyBaseObject_Typeから継承されてい/* tp_getset */ます。フィールドを参照してください。この関数は以下をチェックします。新しいタイプは古いタイプを置き換えることができsome_objますか?
あなたの例を見てみましょう:
class A:
pass
class B:
pass
o = object()
a = A()
b = B()
最初のケース:
a.__class__ = B
aオブジェクトのタイプはA動的に割り当てられるため、ヒープタイプです。同様にB。a型は問題なく変更されます。
2番目のケース:
o.__class__ = B
のタイプoは組み込みタイプobject(PyBaseObject_Type)です。これはヒープタイプではないため、TypeError発生します。
TypeError: __class__ assignment only supported for heap types or ModuleType subclasses.