Bir Python sınıfının __dict __.__ dict__ niteliği nedir?


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?

Yaparsam A.something = 10, bu devreye girer A.__dict__. Ne olduğunu bu <attribute '__dict__' of 'A' objects>bulundu A.__dict__.__dict__o şey içermiyor ne zaman ve?


11
Daha uygun bir örnek değişken olurdu ive. En azından bunu daha fazla A.__dict__['ive']soru haline
Joakim

Yanıtlar:


110

Her şeyden önce A.__dict__.__dict__farklıdır A.__dict__['__dict__']ve birincisi yoktur. İkincisi, __dict__sınıfın örneklerinin sahip olacağı niteliktir. Belirli bir örnek için dahili öznitelik sözlüğünü döndüren bir tanımlayıcı nesnesidir. Kısacası, __dict__bir nesnenin özniteliği nesnelerin içinde saklanamaz __dict__, dolayısıyla ona sınıfta tanımlanan bir tanımlayıcı aracılığıyla erişilir.

Bunu anlamak için , tanımlayıcı protokolün belgelerini okumanız gerekir. .

Kısa versiyon:

  1. Bir sınıf örneği Aiçin , ile aynı olan erişim instance.__dict__sağlanır .A.__dict__['__dict__']vars(A)['__dict__']
  2. A sınıfı, erişim için A.__dict__sağlanır type.__dict__['__dict__']ile aynı olan (teorik olarak) vars(type)['__dict__'].

Uzun versiyon:

Hem sınıflar hem de nesneler, hem öznitelik operatörü (sınıf veya meta sınıflar aracılığıyla uygulanır __getattribute__) hem de __dict__tarafından kullanılan öznitelik / protokol aracılığıyla özniteliklere erişim sağlar vars(ob).

Normal nesneler için __dict__nesne dict, öznitelikleri depolayan ayrı bir nesne oluşturur ve __getattribute__önce ona erişmeye ve buradan öznitelikleri almaya çalışır (tanımlayıcı protokolü kullanarak sınıfta özniteliği aramaya başlamadan önce ve çağırmadan önce __getattr__). __dict__Sınıfına açıklayıcısı bu sözlüğe erişim uygular.

  • x.namesırasına göre söz konusu çalışıyor eşdeğerdir: x.__dict__['name'], type(x).name.__get__(x, type(x)),type(x).name
  • x.__dict__ aynı şeyi yapar ancak bariz nedenlerden dolayı ilkini atlar

Bunun için imkansız gibi __dict__bir instancesaklanacak __dict__örneğinin, doğrudan yerine açıklayıcı protokolü üzerinden erişilen ve örnek özel bir alanda saklanır.

Sınıflar için de benzer bir senaryo geçerlidir, ancak bunların __dict__bir sözlük gibi görünen (ancak dahili olarak olmayabilir) ve onu değiştirmenize veya başka bir tanesiyle değiştirmenize izin vermeyen özel bir proxy nesnesidir. Bu proxy, diğerlerinin yanı sıra, kendisine özgü olan ve temellerinden birinde tanımlanmamış bir sınıfın özniteliklerine erişmenizi sağlar.

Varsayılan olarak, vars(cls)bir boş sınıfın a , dahili olarak kullanılan __dict__örneklerin özniteliklerini ve sınıfın docstringini depolamak için üç tanımlayıcı taşır . Tanımlarsanız ilk ikisi gitmiş olabilir . O zaman ve nitelikleriniz olmazdı , bunun yerine her yuva için tek bir sınıf niteliğiniz olur. Örneğin öznitelikleri bir sözlükte saklanmayacak ve bunlara erişim sınıftaki ilgili tanımlayıcılar tarafından sağlanacaktır.__weakref__weakref__slots____dict____weakref__


Ve son olarak, A.__dict__bundan farklı olan tutarsızlık A.__dict__['__dict__'], özelliğin __dict__istisnai olarak hiçbir zaman aranmamasıdır vars(A), bu yüzden bunun için doğru olan, kullanacağınız diğer herhangi bir özellik için geçerli değildir. Örneğin, A.__weakref__ile aynı şeydir A.__dict__['__weakref__']. Bu tutarsızlık olmasaydı, kullanmak A.__dict__işe yaramazdı ve vars(A)bunun yerine her zaman kullanmanız gerekirdi .


6
Detaylı cevap için teşekkürler. Birkaç kez okumak zorunda olmama rağmen, Python'un bu kadar çok yeni detayını öğrendiğimden bu yana epey zaman geçtiğini düşünüyorum.
porgarmingduod

Neden __dict__bir nesnenin özniteliği nesnenin özniteliği tam olarak depolanamıyor __dict__?
zumgruenenbaum

2
@zumgruenenbaum __dict__Tüm örnek özniteliklerinin saklanması amaçlandığından, formun öznitelik erişimi obj.xsonunda nesneye __dict__, yani nesneye bakılır obj.__dict__['x']. Şimdi, eğer __dict__bir tanımlayıcı olarak uygulanmamışsa, bu sonsuz bir özyinelemeye yol açar, çünkü erişim obj.__dict__için ona bakmanız gerekir obj.__dict__['__dict__']. Tanımlayıcı bu sorunu aşar.
a_guest

11

Yana A.__dict__depolamak sözlüktür Aözelliklerini, A.__dict__['__dict__']aynı direkt referanstır A.__dict__özniteliği.

A.__dict__kendine (türden) bir gönderme içerir. "Tür" kısmı, ifadenin normal yerine a A.__dict__döndürmesinin nedenidir .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__']referans değildir A.__dict__. __dict__Örneklerin özniteliğini uygular . Bunu kendiniz denemek için , başarısız olurken öğesinin A.__dict__['__dict__'].__get__(A(), A)niteliklerini döndürür . A()A.__dict__['__dict__'].__get__(A, type)
Rosh Oksimoron

10

Biraz keşif yapalım!

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

Acaba bu ne?

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

Bir getset_descriptornesnenin hangi nitelikleri vardır?

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

Bunun bir kopyasını alarak dictproxy, özellikle __objclass__ve gibi bazı ilginç nitelikler bulabiliriz __name__.

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

Öyleyse __objclass__bir referans mı Ave __name__sadece bir dizge '__dict__'mi, bir özniteliğin adı mı?

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

İşte bizde var! A.__dict__['__dict__']geri dönebilen bir nesnedir A.__dict__.


PEP 252, bu niteliği tanımlayan__objclass__ sınıfın o sınıfın bir özelliği olmadığını söylüyor. Bu, örneğinizi yanlış yapar . Daha doğru olanıgetattrgetattr(A().__dict__['__dict__'].__objclass__, A.__dict__['__dict__'].__name__)
Rosh Oxymoron

1
@RoshOxymoron İfadeniz KeyError: '__dict__'@ AndrewClark'ın aksine yükseliyor.
Maggyero

9

Daha fazlasını anlamak için aşağıdaki basit örneği deneyebilirsiniz:

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

Yukarıdaki örnekten, sınıf nesnelerinin özniteliklerinin sınıfları tarafından, sınıfın özniteliklerinin ise metasınıflar olan sınıfları tarafından saklandığı görülmektedir. Bu ayrıca aşağıdakiler tarafından da doğrulanır:

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

1
Garip bir şekilde, eğer ikinci karşılaştırmada isikame edilmişse ==, yani A.__dict__ is type.__dict__['__dict__'].__get__(A)sonuç Falsehem python 2.7.15+ hem de 3.6.8'dir.
Arne Vogel
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.