class A(object):
pass
class B(A):
pass
o = object()
a = A()
b = B()
Хотя я могу изменить a.__class__
, я не могу сделать то же самое с o.__class__
(это выдает TypeError
ошибку). Почему?
Например:
isinstance(a, A) # True
isinstance(a, B) # False
a.__class__ = B
isinstance(a, A) # True
isinstance(a, B) # True
isinstance(o, object) # True
isinstance(o, A) # False
o.__class__ = A # This fails and throws a TypeError
# isinstance(o, object)
# isinstance(o, A)
Я знаю, что, как правило, это не очень хорошая идея, так как это может привести к очень странному поведению, если оно обрабатывается неправильно. Это просто ради любопытства.
python
python-3.x
Риккардо Букко
источник
источник
Ответы:
CPython имеет комментарий в Objects / typeobject.c по этой теме:
Объяснение:
CPython хранит объекты двумя способами:
Информация из комментария в Include / object.h .
Когда вы пытаетесь установить новое значение
some_obj.__class__
,object_set_class
вызывается функция. Он унаследован от PyBaseObject_Type , см./* tp_getset */
Поле. Эта функция проверяет : может ли новый тип заменить старый тип вsome_obj
?Возьмите свой пример:
Первый случай:
Тип
a
объекта -A
тип кучи, поскольку он выделяется динамически. Так же как иB
. Вa
типе «S меняется без проблем.Второй случай:
Тип
o
является встроенным типомobject
(PyBaseObject_Type
). Это не тип кучи, поэтомуTypeError
поднимается:источник
Вы можете перейти только
__class__
к другому типу, который имеет такую же внутреннюю (C) компоновку . Среда выполнения даже не знает этот макет, если сам тип не выделен динамически («тип кучи»), поэтому это необходимое условие, которое исключает встроенные типы в качестве источника или назначения. Вы также должны иметь одинаковый набор__slots__
с одинаковыми именами.источник