У версіях 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.
Наскільки нам відомо, весь код, що перебуває за викладеним нижче твердженням "якщо", буде правильно обробляти класи, які не є HEAPTYPE, і перевірка HEAPTYPE потрібна лише для захисту тієї підмножини класів, які не є HEAPTYPE, для яких інтерпретатор припустив, що всі екземпляри справді незмінні.