파이썬 클래스의 __dict __.__ dict__ 속성은 무엇입니까?


92
>>> class A(object): pass
... 
>>> A.__dict__
<dictproxy object at 0x173ef30>
>>> A.__dict__.__dict__
Traceback (most recent call last):
  File "<string>", line 1, in <fragment>
AttributeError: 'dictproxy' object has no attribute '__dict__'
>>> A.__dict__.copy()
{'__dict__': <attribute '__dict__' of 'A' objects> ... }
>>> A.__dict__['__dict__']
<attribute '__dict__' of 'A' objects> # What is this object?

내가 그렇게 A.something = 10하면 A.__dict__. 무엇 이다<attribute '__dict__' of 'A' objects>에서 발견이 A.__dict__.__dict__뭔가를 포함 않을 때, 그리고?


11
더 적합한 예제 변수는 ive. 적어도 이것은 더 많은 A.__dict__['ive']질문을 만들었을 것입니다 ;) 나는 내 자신을 볼 것입니다
Joakim

답변:


110

우선는와 A.__dict__.__dict__다르며 A.__dict__['__dict__']전자는 존재하지 않습니다. 후자는 __dict__클래스의 인스턴스가 가질 수 있는 속성입니다. 특정 인스턴스에 대한 속성의 내부 사전을 반환하는 설명자 개체입니다. 간단히 말해 __dict__객체 의 속성은 객체의에 저장할 수 없으므로 __dict__클래스에 정의 된 설명자를 통해 액세스됩니다.

이것을 이해하려면 디스크립터 프로토콜문서 를 읽어야합니다 .

짧은 버전 :

  1. 클래스 인스턴스의 경우에 대한 A액세스 instance.__dict__가 제공되며 A.__dict__['__dict__']이는과 동일합니다 vars(A)['__dict__'].
  2. 클래스 A의 경우에 대한 액세스 A.__dict__type.__dict__['__dict__'](이론상) vars(type)['__dict__'].

긴 버전 :

클래스와 객체 모두 속성 연산자 (클래스 또는 메타 클래스를 통해 구현 __getattribute__됨)와에서 사용하는 __dict__속성 / 프로토콜을 통해 속성에 대한 액세스를 제공합니다 vars(ob).

일반 객체의 경우 __dict__객체 dict는 속성을 저장 하는 별도 의 객체를 만들고 __getattribute__먼저 액세스를 시도하고 거기에서 속성을 가져옵니다 (설명자 프로토콜을 사용하여 클래스에서 속성을 찾으려고 시도하기 전과를 호출하기 전에 __getattr__). __dict__클래스 의 설명자는이 사전에 대한 액세스를 구현합니다.

  • x.name위해 사람들을 시도하는 것과 같습니다 x.__dict__['name'], type(x).name.__get__(x, type(x)),type(x).name
  • x.__dict__ 동일하지만 명백한 이유로 첫 번째 항목을 건너 뜁니다.

__dict__of instance__dict__인스턴스에 저장할 수 없기 때문에 설명자 프로토콜을 통해 직접 액세스하고 인스턴스의 특수 필드에 저장합니다.

클래스의 경우에도 유사한 시나리오가 적용되지만 __dict__, 사전 인 척하는 특수 프록시 개체 (내부적으로는 아닐 수 있음)이며이를 변경하거나 다른 것으로 대체 할 수 없습니다. 이 프록시를 사용하면 무엇보다도 자신에게 고유하고 기본 중 하나에 정의되지 않은 클래스의 속성에 액세스 할 수 있습니다.

기본적으로 vars(cls)비어있는 클래스의 a 는에서 내부적으로 사용 __dict__하는 인스턴스의 속성 과 클래스의 독 스트링 을 저장하기위한 세 개의 설명자를 전달 합니다. 정의하면 처음 두 개가 사라질 수 있습니다 . 그러면 및 속성이 없지만 대신 각 슬롯에 대해 단일 클래스 속성이 있습니다. 그러면 인스턴스의 속성이 사전에 저장되지 않고 클래스의 각 설명자가 액세스 할 수 있습니다.__weakref__weakref__slots____dict____weakref__


그리고 마지막으로, 불일치 A.__dict__다른는 A.__dict__['__dict__']속성이 있기 때문입니다 __dict__입니다, 예외로, 결코 에서 보지 않았다 vars(A)그래서 무엇 마찬가지입니다 것은 당신이 사용하는 거라고 거의 모든 다른 속성에 대한 사실이 아니다. 예를 들어 A.__weakref__A.__dict__['__weakref__']. 이 불일치가 존재하지 않으면 사용 A.__dict__이 작동하지 않으며 항상 vars(A)대신 사용해야 합니다.


6
자세한 답변에 감사드립니다. 몇 번 읽어야했지만 파이썬에 대한 새로운 세부 사항을 너무 많이 배운 지 오래되었다고 생각합니다.
porgarmingduod

__dict__객체 의 속성을 객체 의 속성에 정확히 저장할 수없는 이유는 무엇 __dict__입니까?
zumgruenenbaum 19

2
@zumgruenenbaum __dict__은 모든 인스턴스 속성을 저장하기위한 것이므로 양식의 속성 액세스 obj.x는 결국 객체의 __dict__, 즉 obj.__dict__['x']. 이제 __dict__설명 자로 구현되지 않은 경우 무한 재귀로 이어질 obj.__dict__것입니다. 액세스 하려면 obj.__dict__['__dict__']. 설명자는이 문제를 우회합니다.
a_guest

11

속성을 A.__dict__저장하는 딕셔너리 이므로 동일한 속성에 대한 직접 참조 입니다.AA.__dict__['__dict__']A.__dict__

A.__dict__자신에 대한 (종류의) 참조를 포함합니다. "종류"부분은 표현식 이 일반 대신 A.__dict__a dictproxy를 반환하는 이유 dict입니다.

>>> class B(object):
...     "Documentation of B class"
...     pass
...
>>> B.__doc__
'Documentation of B class'
>>> B.__dict__
<dictproxy object at 0x00B83590>
>>> B.__dict__['__doc__']
'Documentation of B class'

9
A.__dict__['__dict__']에 대한 참조가 아닙니다 A.__dict__. __dict__인스턴스 의 속성을 구현합니다 . 직접 시도하려면 실패 하는 동안 A.__dict__['__dict__'].__get__(A(), A)의 속성을 반환 합니다. A()A.__dict__['__dict__'].__get__(A, type)
Rosh Oxymoron

10

탐험을 좀 해보자!

>>> A.__dict__['__dict__']
<attribute '__dict__' of 'A' objects>

그게 뭔지 궁금 해요?

>>> type(A.__dict__['__dict__'])
<type 'getset_descriptor'>

getset_descriptor개체에는 어떤 속성 이 있습니까?

>>> type(A.__dict__["__dict__"]).__dict__
<dictproxy object at 0xb7efc4ac>

그 사본을 만들면 dictproxy흥미로운 속성, 특히 __objclass____name__.

>>> A.__dict__['__dict__'].__objclass__, A.__dict__['__dict__'].__name__
(<class '__main__.A'>, '__dict__')

그래서 __objclass__에 대한 참조입니다 A__name__단지 문자열입니다 '__dict__'아마 속성의 이름을?

>>> getattr(A.__dict__['__dict__'].__objclass__, A.__dict__['__dict__'].__name__) == A.__dict__
True

거기에 있습니다! A.__dict__['__dict__']다시 참조 할 수있는 객체입니다.A.__dict__ .


PEP 252는 해당 __objclass__클래스의 속성이 아니라이 속성 을 정의한 클래스 라고 말합니다 . 이것은 귀하의 getattr예를 잘못 만듭니다 . 더 정확한 것은getattr(A().__dict__['__dict__'].__objclass__, A.__dict__['__dict__'].__name__)
Rosh Oxymoron

1
@RoshOxymoron @AndrewClark와는 KeyError: '__dict__'달리 표현 이.
Maggyero

9

다음과 같은 간단한 예제를 통해 더 많은 것을 이해할 수 있습니다.

>>> class A(object): pass
... 
>>> a = A()
>>> type(A)
<type 'type'>
>>> type(a)
<class '__main__.A'>
>>> type(a.__dict__)
<type 'dict'>
>>> type(A.__dict__)
<type 'dictproxy'>
>>> type(type.__dict__)
<type 'dictproxy'>
>>> type(A.__dict__['__dict__'])
<type 'getset_descriptor'>
>>> type(type.__dict__['__dict__'])
<type 'getset_descriptor'>
>>> a.__dict__ == A.__dict__['__dict__'].__get__(a)
True
>>> A.__dict__ == type.__dict__['__dict__'].__get__(A)
True
>>> a.__dict__ == type.__dict__['__dict__'].__get__(A)['__dict__'].__get__(a)
True

위의 예에서 클래스 객체 속성은 클래스별로 저장되고 클래스의 속성은 메타 클래스 인 클래스별로 저장됩니다. 이것은 또한 다음에 의해 검증됩니다.

>>> a.__dict__ == A.__getattribute__(a, '__dict__')
True
>>> A.__dict__ == type.__getattribute__(A, '__dict__')
True

1
이상하게도 두 번째 비교에서 is로 대체 되면 결과는 python 2.7.15+ 및 3.6.8입니다. ==A.__dict__ is type.__dict__['__dict__'].__get__(A)False
Arne Vogel
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.