__dict__
attribute is dict_proxy object in python2 (or mappingproxy object in python3.3+) ?>>> str.__dict__ dict_proxy({'__add__': <slot wrapper '__add__' of 'str' objects>, '__contains__': <slot wrapper '__contains__' of 'str' objects>, ..... 'zfill': <method 'zfill' of 'str' objects>}) >>> type(str.__dict__) <type 'dictproxy'> >>> s = "abc" >>> s.__dict__ Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'str' object has no attribute '__dict__'
在 Python 中對於某些 object __dict__
屬性是隻讀的,好比對於 type object。然而,在 Python2.5-2.6 以前,仍是有一些通常性方法能夠獲取和改變 __dict__
屬性的(without hacking with
gc.get_referrents(), that is)。這會致使一些使人費解的錯誤。python
dictproxy 是爲了用於保證 class.__dict__
的 keys 必須是 strings, proxy 的機制防止了對於 class.__dict__
的寫入操做, 所以只有 setattr() 能夠被用於添加屬性, class.__setattr__
的實現確保了 keys-must-be-strings 的限制.app
若是咱們不使用一些 proxy 的機制,那麼 __dict__
,class.__dict__
就能夠被寫入了。若是能夠寫入,也就能夠被刪除,而 class.__dict__
中的屬性被刪除可能會致使解釋器崩潰。ui
The
__dict__
attribute of some objects is read-only,
e.g. for type objects. However, there is a genericthis
way to still access and modify it (without hacking with
gc.get_referrents(), that is). This can lead to
obscure crashes. Attached is an example that shows
a potential "problem" involving putting strange keys
in the __dict__
of a type.code
This is probably very minor anyway. If we wanted toorm
fix this, we would need a __dict__
descriptor that
looks more cleverly at the object to which it is
applied.對象
BTW the first person who understand why the attachedip
program crashes gets a free coffee.get
------- [Armin Rigo] Bypassing dict readonlyness [Python2.5-2.6]string
__dict__
attribute ?Instances of types defined in C don't have a __dict__
attribute by default.
__dict__['__dict__']
attribute of a Python class?>>> class A(object): x = "1" def __init__(self): self.x = "2" >>> a = A() >>> a.__dict__ {'x': '2'} >>> type(a.__dict__) dict >>> A.__dict__ dict_proxy({'__dict__': <attribute '__dict__' of 'A' objects>, '__doc__': None, '__init__': <function __main__.__init__>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'A' objects>, 'x': '1'}) >>> type(A.__dict__) dict_proxy >>> A.__dict__["__dict__"] <attribute '__dict__' of 'A' objects> >>> type(A.__dict__["__dict__"]) getset_descriptor >>> isinstance(A.__dict__["__dict__"], types.GetSetDescriptorType) True >>> A.__dict__["__dict__"].__get__(a, A) {'x': '2'} >>> a.__dict__ {'x': '2'}
首先,A.__dict__.__dict__
和 A.__dict__['__dict__']
是不一樣的,A.__dict__.__dict__
不存在,A.__dict__['__dict__']
是指 class 的實例擁有的 __dict__
屬性,它是一個描述器,調用它會返回實例的 __dict__
屬性。簡單來講,由於一個實例的 __dict__
屬性不能(why?)保存在實例的 __dict__
中,因此須要經過 class 中的一個 descriptor 來調用。(由於 python 是動態語言嘛,A.__dict__['__dict__']
是一個 GetsetDescriptor,因此實例的屬性是有能力增長的)
a.__dict__
時的背後是經過 A.__dict__['__dict__']
實現的(vars(A)['__dict__']
)A.__dict__
<u>理論上</u> 是經過 type.__dict__['__dict__']
實現的(vars(type)['__dict__']
)完整解釋:
class 和實例訪問屬性都是經過屬性操做符 (class or metaclass's __getattribute__
) 和 __dict__
屬性/協議實現的。
對於通常的實例對象,__dict__
會返回一個保存包含全部實例屬性的獨立的 dict 實例對象,對 __getattribute__
的調用首先會訪問這個 dict,並獲取相應的實例屬性 (這個調用會在經過描述器協議訪問 class 屬性以前,也會在調用 __getattr__
以前)。class 裏定義的 __dict__
描述器實現了對這個 dict 的訪問。
x.name
的調用會按照如下順序: x.__dict__['name']
, type(x).name.__get__(x, type(x))
, type(x).name
x.__dict__
會按照一樣順序,可是很明顯會跳過 x.__dict__['name']
的訪問。由於 x.__dict__
不能保存在 x.__dict__["__dict__"]
中,對於 x.__dict__
的訪問就會用描述器協議實現,x.__dict__
的值就會保存在實例中的一個特殊字段裏。
對於 class 也會面臨相同的狀況,雖然 class.__dict__
是一個假裝成 dict 的特殊的 proxy 對象,class.__dict__
也不容許你對它進行
修改或替換行爲。這個特殊的 proxy 對象容許你,獲取那些定義在 class 而不是 class 的基類中的的屬性。
默認狀況下,vars(cls)
對於一個空類型,返回的對象包含三個描述器,__dict__
用於保存實例中的屬性,__weakref__
是用於 weakref 模塊的內部邏輯,__doc__
是用於 class 的 docstring。前兩個描述器可能會由於定義了 __slots__
而消失,沒有 __dict__
and __weakref__
屬性,反而會有每個定義在 __slots__
的屬性。此時,實例的屬性不會保存在 dict 中,訪問屬性將會經過相應的描述器實現。
refs: What is the dict__.__dict attribute of a Python class?
# -*- encoding: utf -*- class RevealAccess(object): """A data descriptor that sets and returns values normally and prints a message logging their access. """ def __init__(self, initval=None, name='var'): self.val = initval self.name = name def __get__(self, obj, objtype): print('Retrieving', self.name, self.val) return self.val def __set__(self, obj, val): print('Updating', self.name, self.val) self.val = val class Base(object): attr_1 = RevealAccess(10, 'var "x"') def __init__(self): self.attr_2 = RevealAccess(10, 'var "x"') def __getattribute__(self, *args, **kwargs): print("__getattribute__", args, kwargs) return super(Base, self).__getattribute__(*args, **kwargs) def __getattr__(self, *args, **kwargs): print("__getattr__", args, kwargs) try: origin = super(Base, self).__getattr__(*args, **kwargs) return origin except AttributeError as e: return "not found" def main(): b = Base() print("*********** start get b.attr_1 ***********") print(b.attr_1) print("*********** start get b.attr_2 ***********") print(b.attr_2) print("*********** start get b.attr_3 ***********") print(b.attr_3) if __name__ == '__main__': main() Output: *********** start get b.attr_1 *********** ('__getattribute__', ('attr_1',), {}) ('Retrieving', 'var "x"', 10) 10 *********** start get b.attr_2 *********** ('__getattribute__', ('attr_2',), {}) <__main__.RevealAccess object at 0x100b1abd0> *********** start get b.attr_3 *********** ('__getattribute__', ('attr_3',), {}) ('__getattr__', ('attr_3',), {}) not found
Refs: