CPython có một nhận xét trong Đối tượng / typeobject.c về chủ đề này:
Trong các phiên bản CPython trước 3.5, mã trong
compatible_for_assignmentkhông được thiết lập để kiểm tra chính xác khả năng tương thích của bố cục / khe cắm / bộ nhớ đối với các lớp không phải HEAPTYPE, vì vậy chúng tôi chỉ không cho phép __class__gán trong mọi trường hợp không phải là HEAPTYPE -> HEAPTYPE.
Trong chu kỳ phát triển 3.5, chúng tôi đã sửa mã
compatible_for_assignmentđể kiểm tra chính xác khả năng tương thích giữa các loại tùy ý và bắt đầu cho phép __class__gán trong tất cả các trường hợp trong đó các loại cũ và mới thực tế có các khe cắm và bố cục bộ nhớ tương thích (bất kể chúng có được triển khai như HEAPTYPE không hay không).
Tuy nhiên, ngay trước khi 3.5 được phát hành, chúng tôi đã phát hiện ra rằng điều này dẫn đến các vấn đề với các loại bất biến như int, trong đó trình thông dịch giả định rằng chúng là bất biến và thực hiện một số giá trị. Trước đây đây không phải là một vấn đề, bởi vì chúng thực sự là bất biến - đặc biệt, tất cả các loại mà trình thông dịch áp dụng thủ thuật thực tập này cũng được phân bổ tĩnh, vì vậy các quy tắc HEAPTYPE cũ đã "vô tình" ngăn chúng không cho phép __class__gán. Nhưng với những thay đổi về __class__chuyển nhượng, chúng tôi đã bắt đầu cho phép mã như
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
(xem https://bugs.python.org/su24912 ).
Về lý thuyết, cách khắc phục thích hợp sẽ là xác định các lớp nào dựa vào bất biến này và bằng cách nào đó không cho phép __class__gán cho chúng, có lẽ thông qua một số cơ chế như cờ Py_TPFLAGS_IMMUTABLE mới (cách tiếp cận "danh sách đen"). Nhưng trên thực tế, vì vấn đề này không được phát hiện muộn trong chu kỳ 3.5 RC, chúng tôi đang thực hiện phương pháp bảo thủ và khôi phục cùng một kiểm tra HEAPTYPE-> HEAPTYPE mà chúng tôi từng có, cộng với "danh sách trắng". Hiện tại, danh sách trắng chỉ bao gồm các kiểu con ModuleType, vì đó là những trường hợp thúc đẩy bản vá ở vị trí đầu tiên - xem https://bugs.python.org/su22986 - và vì các đối tượng mô-đun có thể thay đổi, chúng tôi có thể chắc chắn rằng họ chắc chắn không được thực tập. Vì vậy, bây giờ chúng tôi cho phép HEAPTYPE-> HEAPTYPE hoặc
Loại phụ ModuleType -> Loại phụ ModuleType.
Theo như chúng tôi biết, tất cả các mã nằm ngoài câu lệnh 'if' sau đây sẽ xử lý chính xác các lớp không phải HEAPTYPE và kiểm tra HEAPTYPE chỉ cần để bảo vệ tập hợp con của các lớp không phải HEAPTYPE mà trình thông dịch đã nướng trong giả định rằng tất cả các trường hợp là thực sự bất biến.
Giải trình:
CPython lưu trữ các đối tượng theo hai cách:
Đối tượng là các cấu trúc được phân bổ trên heap. Các quy tắc đặc biệt áp dụng cho việc sử dụng các đối tượng để đảm bảo chúng được thu gom rác đúng cách. Các đối tượng không bao giờ được phân bổ tĩnh hoặc trên ngăn xếp; chúng phải được truy cập thông qua các macro và chức năng đặc biệt. (Các đối tượng loại là ngoại lệ cho quy tắc đầu tiên; các loại tiêu chuẩn được biểu thị bằng các đối tượng loại khởi tạo tĩnh, mặc dù hoạt động trên thống nhất loại / lớp cho Python 2.2 cũng có thể có các đối tượng loại được phân bổ heap).
Thông tin từ nhận xét trong Bao gồm / object.h .
Khi bạn đang cố gắng đặt giá trị mới some_obj.__class__, object_set_classhàm sẽ được gọi. Nó được kế thừa từ PyBaseObject_Type , xem /* tp_getset */trường. Chức năng này kiểm tra : loại mới có thể thay thế loại cũ trong some_objkhông?
Lấy ví dụ của bạn:
class A:
pass
class B:
pass
o = object()
a = A()
b = B()
Trường hợp đầu tiên:
a.__class__ = B
Loại ađối tượng là A, loại heap, bởi vì nó được phân bổ động. Cũng như B. Các a's loại được thay đổi mà không có một vấn đề.
Trường hợp thứ hai:
o.__class__ = B
Loại olà loại tích hợp object( PyBaseObject_Type). Nó không phải là loại heap, vì vậy, TypeErrorđược nâng lên:
TypeError: __class__ assignment only supported for heap types or ModuleType subclasses.