>>> 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?
Jika saya lakukan A.something = 10
, ini masuk ke A.__dict__
. Apa yang ini <attribute '__dict__' of 'A' objects>
berhasil ditemukan di A.__dict__.__dict__
, dan ketika tidak mengandung sesuatu?
python
class
metaprogramming
magic-methods
porgarmingduod
sumber
sumber
ive
. Setidaknya itu akan membuat ini menjadiA.__dict__['ive']
pertanyaan lagi ;) Saya akan melihat diri saya keluarJawaban:
Pertama-tama
A.__dict__.__dict__
berbeda dariA.__dict__['__dict__']
, dan yang pertama tidak ada. Yang terakhir adalah__dict__
atribut yang dimiliki oleh instance kelas. Ini adalah objek deskriptor yang mengembalikan kamus internal atribut untuk instance tertentu. Singkatnya,__dict__
atribut suatu objek tidak dapat disimpan dalam objek__dict__
, jadi itu diakses melalui deskriptor yang ditentukan di kelas.Untuk memahami ini, Anda harus membaca dokumentasi protokol deskriptor .
Versi singkatnya:
A
, akses keinstance.__dict__
disediakan olehA.__dict__['__dict__']
yang sama denganvars(A)['__dict__']
.A.__dict__
disediakan olehtype.__dict__['__dict__']
(secara teori) yang sama denganvars(type)['__dict__']
.Versi panjang:
Baik kelas maupun objek menyediakan akses ke atribut baik melalui operator atribut (diimplementasikan melalui kelas atau metaclass
__getattribute__
), dan__dict__
atribut / protokol yang digunakan olehvars(ob)
.Untuk objek normal,
__dict__
objek membuat objek terpisahdict
, yang menyimpan atribut, dan__getattribute__
pertama kali mencoba mengaksesnya dan mendapatkan atribut dari sana (sebelum mencoba mencari atribut di kelas dengan memanfaatkan protokol deskriptor, dan sebelum memanggil__getattr__
). The__dict__
descriptor pada kelas mengimplementasikan akses ke kamus ini.x.name
setara dengan mencoba mereka dalam rangka:x.__dict__['name']
,type(x).name.__get__(x, type(x))
,type(x).name
x.__dict__
melakukan hal yang sama tetapi melewatkan yang pertama karena alasan yang jelasSeperti tidak mungkin untuk
__dict__
dariinstance
untuk disimpan dalam__dict__
instance, itu diakses melalui protokol deskriptor langsung sebaliknya, dan disimpan dalam bidang khusus dalam hal ini.Skenario serupa berlaku untuk kelas, meskipun mereka
__dict__
adalah objek proxy khusus yang berpura-pura menjadi kamus (tetapi mungkin tidak secara internal), dan tidak memungkinkan Anda untuk mengubahnya atau menggantinya dengan yang lain. Proksi ini memungkinkan Anda, di antara yang lainnya, untuk mengakses atribut kelas yang khusus untuk itu, dan tidak ditentukan di salah satu basisnya.Secara default,
vars(cls)
kelas kosong membawa tiga deskriptor -__dict__
untuk menyimpan atribut instance,__weakref__
yang digunakan secara internal olehweakref
, dan docstring kelas. Dua yang pertama mungkin hilang jika Anda menentukan__slots__
. Maka Anda tidak akan memiliki__dict__
dan__weakref__
atribut, tetapi Anda akan memiliki atribut kelas tunggal untuk setiap slot. Atribut instance tidak akan disimpan dalam kamus, dan akses ke sana akan disediakan oleh deskriptor masing-masing di kelas.Dan terakhir, ketidakkonsistenan yang
A.__dict__
berbeda dariA.__dict__['__dict__']
adalah karena atribut__dict__
, dengan pengecualian, tidak pernah dicarivars(A)
, jadi apa yang benar karena itu tidak benar untuk hampir semua atribut lain yang Anda gunakan. Misalnya,A.__weakref__
sama saja denganA.__dict__['__weakref__']
. Jika ketidakkonsistenan ini tidak ada, penggunaanA.__dict__
tidak akan berhasil, dan Anda harus selalu menggunakannyavars(A)
.sumber
__dict__
atribut suatu objek tidak dapat disimpan di objek tersebut__dict__
?__dict__
dimaksudkan untuk menyimpan semua atribut instance, akses atribut dari formulirobj.x
akhirnya dicari pada objek__dict__
, yaituobj.__dict__['x']
. Sekarang jika__dict__
tidak diimplementasikan sebagai deskriptor, ini akan mengarah pada rekursi tak terbatas, karena untuk mengaksesobj.__dict__
Anda harus mencarinya sebagaiobj.__dict__['__dict__']
. Deskriptor menghindari masalah ini.Karena
A.__dict__
kamus menyimpanA
atribut,A.__dict__['__dict__']
adalah referensi langsung keA.__dict__
atribut yang sama .A.__dict__
berisi referensi (jenis) untuk dirinya sendiri. Bagian "kind-of" adalah alasan ekspresiA.__dict__
mengembalikan a,dictproxy
bukan normaldict
.>>> 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'
sumber
A.__dict__['__dict__']
bukan referensi keA.__dict__
. Ini mengimplementasikan__dict__
atribut instance. Untuk mencobanya sendiri,A.__dict__['__dict__'].__get__(A(), A)
kembalikan atributA()
, sementaraA.__dict__['__dict__'].__get__(A, type)
gagal.Mari kita menjelajah!
>>> A.__dict__['__dict__'] <attribute '__dict__' of 'A' objects>
Aku penasaran apa itu?
>>> type(A.__dict__['__dict__']) <type 'getset_descriptor'>
Atribut apa yang
getset_descriptor
dimiliki suatu benda?>>> type(A.__dict__["__dict__"]).__dict__ <dictproxy object at 0xb7efc4ac>
Dengan membuat salinannya
dictproxy
kita dapat menemukan beberapa atribut yang menarik, khususnya__objclass__
dan__name__
.>>> A.__dict__['__dict__'].__objclass__, A.__dict__['__dict__'].__name__ (<class '__main__.A'>, '__dict__')
Jadi,
__objclass__
apakah referensi keA
dan__name__
hanya string'__dict__'
, nama atribut mungkin?>>> getattr(A.__dict__['__dict__'].__objclass__, A.__dict__['__dict__'].__name__) == A.__dict__ True
Itu dia!
A.__dict__['__dict__']
adalah objek yang bisa dirujuk kembaliA.__dict__
.sumber
__objclass__
adalah kelas yang mendefinisikan atribut ini, bukan itu adalah atribut kelas itu. Ini membuatgetattr
contoh Anda salah. Yang lebih benar adalahgetattr(A().__dict__['__dict__'].__objclass__, A.__dict__['__dict__'].__name__)
KeyError: '__dict__'
, bertentangan dengan @ AndrewClark.Anda dapat mencoba contoh sederhana berikut untuk lebih memahami ini:
>>> 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
Dari contoh di atas, terlihat bahwa atribut objek kelas disimpan oleh kelasnya, atribut kelas disimpan oleh kelasnya, yaitu metaclass. Ini juga divalidasi oleh:
>>> a.__dict__ == A.__getattribute__(a, '__dict__') True >>> A.__dict__ == type.__getattribute__(A, '__dict__') True
sumber
is
diganti==
dalam perbandingan kedua, yaituA.__dict__ is type.__dict__['__dict__'].__get__(A)
, hasilnya adaFalse
di python 2.7.15+ dan 3.6.8.