पायथन वर्ग की __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']सवाल बना दिया होता ;) मैं खुद को बाहर देखता
जोकिम

जवाबों:


110

सबसे पहले A.__dict__.__dict__अलग है A.__dict__['__dict__'], और पूर्व मौजूद नहीं है। उत्तरार्द्ध वह __dict__विशेषता है जो वर्ग के उदाहरणों में होगी। यह एक वर्णनात्मक वस्तु है जो विशिष्ट उदाहरण के लिए विशेषताओं का आंतरिक शब्दकोश लौटाता है। संक्षेप में, __dict__किसी ऑब्जेक्ट की विशेषता को ऑब्जेक्ट में संग्रहीत नहीं किया जा सकता है __dict__, इसलिए इसे कक्षा में परिभाषित एक डिस्क्रिप्टर के माध्यम से एक्सेस किया जाता है।

इसे समझने के लिए, आपको डिस्क्रिप्टर प्रोटोकॉल के प्रलेखन को पढ़ना होगा ।

लघु संस्करण:

  1. कक्षा के उदाहरण के लिए A, इसके लिए पहुँच instance.__dict__प्रदान की जाती है, A.__dict__['__dict__']जो उसी के समान है vars(A)['__dict__']
  2. वर्ग ए के लिए, (सिद्धांत में) 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__की instanceमें संग्रहीत करने के लिए __dict__उदाहरण के लिए, यह वर्णनकर्ता प्रोटोकॉल के माध्यम से करने के बजाय सीधे पहुंचा जा सकता है, और उदाहरण में एक विशेष क्षेत्र में संग्रहित है।

एक समान परिदृश्य कक्षाओं के लिए सच है, हालांकि उनकी __dict__एक विशेष प्रॉक्सी ऑब्जेक्ट है जो एक शब्दकोश होने का दिखावा करता है (लेकिन आंतरिक रूप से नहीं हो सकता है), और आपको इसे बदलने या इसे किसी अन्य के साथ बदलने की अनुमति नहीं देता है। यह प्रॉक्सी आपको और सभी के बीच, उस वर्ग की विशेषताओं को एक्सेस करने की अनुमति देता है जो इसके लिए विशिष्ट हैं, और इसके एक आधार में परिभाषित नहीं है।

डिफ़ॉल्ट रूप से, एक vars(cls)खाली वर्ग के तीन वर्णकों को वहन करता है - __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

2
@zumgruenenbaum चूंकि __dict__सभी इंस्टेंस विशेषताओं को संग्रहीत करने के लिए है, फॉर्म की एक विशेषता एक्सेस obj.xअंततः ऑब्जेक्ट __dict__(अर्थात्) पर दिखाई देती है obj.__dict__['x']। अब यदि __dict__इसे एक विवरणक के रूप में लागू नहीं किया गया था, तो यह एक अनंत पुनरावृत्ति का कारण बनेगा, क्योंकि उपयोग करने के लिए obj.__dict__आपको इसे देखने की आवश्यकता होगी obj.__dict__['__dict__']। विवरणक इस समस्या को दरकिनार करता है।
a_guest

11

चूँकि A.__dict__एक डिक्शनरी संग्रह की Aविशेषता है, A.__dict__['__dict__']उसी A.__dict__विशेषता का सीधा संदर्भ है ।

A.__dict__अपने आप में (तरह का) संदर्भ समाहित करता है। "दयालु" भाग ऐसा क्यों है कि अभिव्यक्ति एक सामान्य के बजाय A.__dict__वापस आती है ।dictproxydict

>>> 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)है।
रोश ऑक्सीमोरोन

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__)
रोश ऑक्सीमोरोन

1
@RoshOxymoron आपकी अभिव्यक्ति में वृद्धि होती है KeyError: '__dict__', @ एंड्रयूक्लार्क के विपरीत।
मैगीयरो

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लिए प्रतिस्थापित किया ==जाता है, यानी A.__dict__ is type.__dict__['__dict__'].__get__(A), परिणाम Falseदोनों अजगर 2.7.15+ और 3.6.8 में है।
अर्ने वोगेल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.