2
from xml.etree.ElementTree import Element
e = Element(None)
e.__dict__

-> AttributeError: 'xml.etree.ElementTree.Element' object has no attribute '__dict'

Copy C:\Python39\Lib\xml\etree\ElementTree.py somewhere else:

from ElementTree import Element as Element2
e2 = Element2(None)
e2.__dict__

-> {'tag': None, 'attrib': {}, '_children': []}

Stefan
  • 4,380
  • 2
  • 30
  • 33

2 Answers2

2

This answer explains well the __dict__ attribute:

Python objects store their instance variables in a dictionary that belongs to the object. vars(x) returns this dictionary (as does x.__dict__). dir(x), on the other hand, returns a dictionary of x's "attributes, its class's attributes, and recursively the attributes of its class's base classes."

So let's see this in practice:

import xml.etree.ElementTree as ET

e = ET.Element(None)
print(e.__dict__)
# Raises AttributeError: 'xml.etree.ElementTree.Element' object has no attribute '__dict__'

But why, since e does have attributes?

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

Let's see the attributes from its class:

import xml.etree.ElementTree as ET

print(ET.Element.__dict__)

This will output

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

So tag is not an instance variable, it's a class variable, that's why it's present in the class' __dict__, not in the e's __dict__.

In fact, the instance of ET.Element (e) does not have any instance variables, but only class variables defined in the ET.Element.


I'm not sure about why the library class doesn't have a __dict__ and a user defined class has, but I think it's related to this answer:

[...] These are usually natively coded (i.e. written in C) - and the __dict__ attribute would have to be explicitly created - so unlike classes coded in Python - one have to go out of his way to allow __dict__ in these classes, while for Python code, the detour is on avoiding __dict__.

enzo
  • 9,861
  • 3
  • 15
  • 38
  • "In fact, the instance of ET.Element (e) does not have any instance variables, but only class variables defined in the ET.Element." That's not correct as such. What you are seeing on the class are *slots*, which encode how to access the attribute directly in the memory of the instance. These are similar to a ``property``, which exists on the class but dictates access to instance variables. – MisterMiyagi Jul 18 '21 at 17:26
2

The xml.etree.ElementTree has an optimised C implementation which replaces the Python implementation if available:

# Import the C accelerators
try:
    # Element is going to be shadowed by the C implementation. We need to keep
    # the Python version of it accessible for some "creative" by external code
    # (see tests)
    _Element_Py = Element

    # Element, SubElement, ParseError, TreeBuilder, XMLParser, _set_factories
    from _elementtree import *
    from _elementtree import _set_factories
except ImportError:
    pass
else:
    _set_factories(Comment, ProcessingInstruction)

Notably, C extension types generally do not have a __dict__ slot. By copying the Python implementation of the class without loading of the C implementation, one is using the original Python implementation.

>>> from xml.etree.ElementTree import _Element_Py
>>> e = _Element_Py(None)
>>> e.__dict__
{'tag': None, 'attrib': {}, '_children': []}
MisterMiyagi
  • 44,374
  • 10
  • 104
  • 119