객체 인스턴스의 __class__ 속성을 변경할 수없는 이유는 무엇입니까?


10
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)

나는 이것이 잘못 처리되면 매우 이상한 행동을 일으킬 수 있기 때문에 일반적으로 좋은 생각이 아니라는 것을 알고 있습니다. 호기심을 위해서입니다.


3
내장 타입은 효율성 때문에 사용자 정의 타입의 역 동성을 희생합니다. 또 다른 선택적 최적화는 슬롯이며, 이와 유사하게이를 방지합니다.
juanpa.arrivillaga

답변:


6

CPython 에는 이 주제에 대한 Objects / typeobject.c 에 주석 이 있습니다.

3.5 이전의 CPython 버전에서는 compatible_for_assignment비 HEAPTYPE 클래스의 메모리 레이아웃 / 슬롯 등 호환성을 올바르게 확인 하도록 코드 가 설정 __class__되지 않았으므로 HEAPTYPE-> HEAPTYPE이 아닌 경우에는 할당을 허용하지 않았습니다.

3.5 개발주기 동안 compatible_for_assignment임의 유형 간의 호환성을 올바르게 확인하기 위해 코드를 수정하고 __class__이전 유형과 새 유형이 실제로 호환 가능한 슬롯과 메모리 레이아웃을 가진 모든 경우에 할당을 허용하기 시작했습니다 (HEAPTYPE으로 구현되었는지 여부에 관계없이). 또는 아닙니다).

그러나 3.5가 출시되기 직전에 우리는 이것이 int와 같은 불변 유형에 문제가 있음을 발견했습니다. 여기서 인터프리터는 불변이라고 가정하고 일부 값을 인턴합니다. 이전에는 이것이 실제로 불변 이었기 때문에 문제가되지 않았습니다. 특히 인터프리터가이 interning 트릭을 적용한 모든 유형도 정적으로 할당 되었기 때문에 기존 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.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의 유형은 문제없이 변경됩니다.

두 번째 경우 :

o.__class__ = B

유형은 o내장 유형 object( PyBaseObject_Type)입니다. 힙 유형이 아니므 TypeError로 발생합니다.

TypeError: __class__ assignment only supported for heap types or ModuleType subclasses.

4

__class__내부 (C) 레이아웃 이 동일한 다른 유형으로 만 변경할 수 있습니다 . 런타임 자체는 유형 자체가 동적으로 할당되지 않으면 (“힙 유형”) 레이아웃을 알지 못하므로 내장 유형을 소스 또는 대상으로 제외하는 필수 조건입니다. 또한 __slots__동일한 이름 을 가진 동일한 세트를 가져야합니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.