В версиях CPython до 3.5 код
compatible_for_assignmentне был настроен для правильной проверки совместимости макета памяти, слотов и т. Д. Для классов, отличных от HEAPTYPE, поэтому мы просто запретили __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, для которых интерпретатор запекся в предположении, что все случаи действительно неизменны.