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