5

I don't understand why in Python 3 I can't add some attributes to ElementTree.Element instances. Here is the difference:

In Python 2:

Python 2.6.6 (r266:84292, Jun 18 2012, 14:18:47) 
[GCC 4.4.6 20110731 (Red Hat 4.4.6-3)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from xml.etree import ElementTree as ET
>>> el = ET.Element('table')
>>> el.foo = 50
>>> el.foo
50
>>> 

In Python 3:

Python 3.3.0 (default, Sep 11 2013, 16:29:08) 
[GCC 4.4.6 20120305 (Red Hat 4.4.6-4)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from xml.etree import ElementTree as ET
>>> el = ET.Element('table')
>>> el.foo = 50
>>> el.foo
AttributeError: foo
>>> 

Python 2 is provided by a distribution (CentOS). Python 3 was compiled from sources.

Is it intended behavior, a bug, or do I have to recompile python 3 with some additional flags?

UPDATE:

Some clarification: I am trying to set attributes on Python object, i.e. on Element instance. Not XML attributes (Element.attrib).

This issue actually arose when I tried to subclass Element. Here is example:

>>> class Table(ET.Element):
...     def __init__(self):
...         super().__init__('table')
...         print('calling __init__')
...         self.foo = 50
... 
>>> t = Table()
calling __init__
>>> t.foo
Traceback (most recent call last):
  File "<input>", line 1, in <module>
AttributeError: 'Table' object has no attribute 'foo'
>>> 

This makes me think that Element class is instantiated in some tricky way, but I can't figure out what goes on. Hence the question.

Maxim
  • 1,783
  • 2
  • 14
  • 24
  • i'm not using python 3 (still on 2) but can you try dumping t.__dict__ and see if foo is even in there? – Corley Brigman Jan 08 '14 at 15:25
  • again: see the first link in my answer, and please check whether the python 3 object returned has a `__dict__` attribute (and possibly equivalently, if it has a `__slots__` attribute). Using `__slots__` leads to much better memory performance, but has the side effect that you can't add attributes the normal way (and since etree is [was] a memory hog, this is exactly what i'd expect them to do). If it _does_ have slots, you just need to declare them in your object as well, with a `__slots__` declaration for the new fields you want, and then it should work. (if that's the problem..) – Corley Brigman Apr 24 '15 at 18:06

3 Answers3

1

It's probably intentional... take a look at Can't set attributes of object class . If they haven't enhanced ElementTree to use slots instead of dict in that time, i would be surprised anyways.

Not clear what you are trying to do... are you really trying to set python attributes, or XML attributes? If the latter, you really want to do this:

el = ET.Element('table')
el.set('foo', 50) 
#or
el.attrib['foo'] = 50

If you really want to add python attributes, you should subclass instead, and probably provide your own Element/SubElement functions to supply the 'wrapped' elements instead of standard ones.

Update 6/4/2016: Maybe not clear from my answer, but here's what you probably need to do (I'm normally python 2.7):

class Table(ET.Element):
    # Adding __slots__ gives it a known attribute to use    
    __slots__ = ('foo',)
    def __init__(self):
        super().__init__('table')
        print('calling __init__')
        self.foo = 50
Community
  • 1
  • 1
Corley Brigman
  • 11,633
  • 5
  • 33
  • 40
  • 2
    See update to my question. I try to set attributes on python object, not XML attributes. And I tried to subclass ``Element``, this doesn't seem to work. – Maxim Jan 08 '14 at 15:14
1

Since 3.3 ElementTree tries to import the c implementation in for efficiency, however you can't set arbitrary attributes on that implementation. If you are in a situation where you don't want to use the Set or Get methods every time, you should use ET._Element_Py, which is the Python implementation.

RandomName
  • 11
  • 1
0

According to Python 3 documentation, it seems that the only way to go is now:

>>> from xml.etree import ElementTree as ET
>>> el = ET.Element('table')
>>> el.set("foo", 50)
>>> el.get("foo")
50
>>> el.attrib
{"foo": 50}

It worked under Python 2.X too but is probably mandatory now.

Also, an interesting ticket in Python bug tracker where they decided to change the exception message.

filaton
  • 2,257
  • 17
  • 27