This answer 很好地解释了__dict__ 属性:
Python 对象将它们的实例变量存储在属于该对象的字典中。 vars(x) 返回这个字典(和 x.__dict__ 一样)。另一方面,dir(x) 返回 x 的“属性、其类的属性以及递归其类的基类的属性”的字典。
让我们在实践中看看:
import xml.etree.ElementTree as ET
e = ET.Element(None)
print(e.__dict__)
# Raises AttributeError: 'xml.etree.ElementTree.Element' object has no attribute '__dict__'
但是为什么,既然e 确实有属性呢?
import xml.etree.ElementTree as ET
e = ET.Element(None)
print(e.tag)
# Outputs None
e = ET.Element("my-tag")
print(e.tag)
# Outputs my-tag
让我们看看它的类的属性:
import xml.etree.ElementTree as ET
print(ET.Element.__dict__)
这将输出
mappingproxy({'__copy__': <method '__copy__' of 'xml.etree.ElementTree.Element' objects>,
'__deepcopy__': <method '__deepcopy__' of 'xml.etree.ElementTree.Element' objects>,
'__delitem__': <slot wrapper '__delitem__' of 'xml.etree.ElementTree.Element' objects>,
'__doc__': None,
'__getattribute__': <slot wrapper '__getattribute__' of 'xml.etree.ElementTree.Element' objects>,
'__getitem__': <slot wrapper '__getitem__' of 'xml.etree.ElementTree.Element' objects>,
'__getstate__': <method '__getstate__' of 'xml.etree.ElementTree.Element' objects>,
'__init__': <slot wrapper '__init__' of 'xml.etree.ElementTree.Element' objects>,
'__len__': <slot wrapper '__len__' of 'xml.etree.ElementTree.Element' objects>,
'__new__': <built-in method __new__ of type object at 0x92a480>,
'__repr__': <slot wrapper '__repr__' of 'xml.etree.ElementTree.Element' objects>,
'__setitem__': <slot wrapper '__setitem__' of 'xml.etree.ElementTree.Element' objects>,
'__setstate__': <method '__setstate__' of 'xml.etree.ElementTree.Element' objects>,
'__sizeof__': <method '__sizeof__' of 'xml.etree.ElementTree.Element' objects>,
'append': <method 'append' of 'xml.etree.ElementTree.Element' objects>,
'attrib': <attribute 'attrib' of 'xml.etree.ElementTree.Element' objects>,
'clear': <method 'clear' of 'xml.etree.ElementTree.Element' objects>,
'extend': <method 'extend' of 'xml.etree.ElementTree.Element' objects>,
'find': <method 'find' of 'xml.etree.ElementTree.Element' objects>,
'findall': <method 'findall' of 'xml.etree.ElementTree.Element' objects>,
'findtext': <method 'findtext' of 'xml.etree.ElementTree.Element' objects>,
'get': <method 'get' of 'xml.etree.ElementTree.Element' objects>,
'getchildren': <method 'getchildren' of 'xml.etree.ElementTree.Element' objects>,
'getiterator': <method 'getiterator' of 'xml.etree.ElementTree.Element' objects>,
'insert': <method 'insert' of 'xml.etree.ElementTree.Element' objects>,
'items': <method 'items' of 'xml.etree.ElementTree.Element' objects>,
'iter': <method 'iter' of 'xml.etree.ElementTree.Element' objects>,
'iterfind': <method 'iterfind' of 'xml.etree.ElementTree.Element' objects>,
'itertext': <method 'itertext' of 'xml.etree.ElementTree.Element' objects>,
'keys': <method 'keys' of 'xml.etree.ElementTree.Element' objects>,
'makeelement': <method 'makeelement' of 'xml.etree.ElementTree.Element' objects>,
'remove': <method 'remove' of 'xml.etree.ElementTree.Element' objects>,
'set': <method 'set' of 'xml.etree.ElementTree.Element' objects>,
'tag': <attribute 'tag' of 'xml.etree.ElementTree.Element' objects>,
'tail': <attribute 'tail' of 'xml.etree.ElementTree.Element' objects>,
'text': <attribute 'text' of 'xml.etree.ElementTree.Element' objects>})
所以tag 不是实例变量,它是类变量,这就是为什么它出现在类的__dict__ 中,而不是出现在e 的__dict__ 中。
其实ET.Element(e)的实例没有任何实例变量,只有ET.Element中定义的类变量。
我不确定为什么库类没有__dict__ 而用户定义的类有,但我认为它与this answer 有关:
[...] 这些通常是本机编码的(即用 C 编写的)——并且 __dict__ 属性必须被显式创建——因此与用 Python 编码的类不同——必须不遗余力地允许 __dict__ 进入这些类,而对于 Python 代码,绕道是避免 __dict__。