Reputation: 26812
I have a Python class for a Person
object which inherits xml.etree.ElementTree.Element
.
from xml.etree import ElementTree
class Person(ElementTree.Element):
def __init__(self, name, age):
super().__init__("person", name=name, age=str(age))
person = Person("Paul", 23)
I plan on expanding my Person
class to include additional attributes and subtags. Before doing this, I want to simplify attribute access by converting my class to use lxml.objectify.Element
.
from lxml import objectify
class Person(objectify.Element):
def __init__(self, name, age):
super().__init__("person", name=name, age=str(age))
person = Person("Paul", 23)
Unfortunately, trying to inherit from objectify.Element
raises a TypeError about creating cython_function_or_method
instances.
Traceback (most recent call last):
File "C:/Users/svascellar/.PyCharmCE2017.3/config/scratches/scratch_1.py", line 2, in <module>
class Person(objectify.Element):
TypeError: cannot create 'cython_function_or_method' instances
Why is my class raising a TypeError? How do I inherit frrom lxml.objectify.Element
?
Upvotes: 1
Views: 1402
Reputation: 26812
lxml
to create subclasses.As the comments mentioned, lxml.objectify.Element()
is not a class. It is a factory method that creates and returns an object, meaning it cannot be inherited.
While it's possible to inherit from ElementBase
or ObjectifiedElement
, the lxml documentation strongly warns that you must not override __init__
or __new__
, which my original answer did.
BIG FAT WARNING: Subclasses must not override
__init__
or__new__
as it is absolutely undefined when these objects will be created or destroyed. All persistent state of Elements must be stored in the underlying XML. If you really need to initialize the object after creation, you can implement an _init(self) method that will be called directly after object creation.
- Documentation forlxml.etree.ElementBase
I suggest inheriting from a different class instead. As mentioned in the original question, you can inherit from lxml.etree.ElementTree.Element
fairly easily.
from xml.etree import ElementTree
class Person(ElementTree.Element):
def __init__(self, name, age):
super().__init__("person", name=name, age=str(age))
person = Person("Paul", 23)
Working with sub-classes of lxml
may be more trouble than it's worth. However, if you really want to subclass lxml
despite the warnings, you can see my original answer below.
Try inheriting from lxml.objectify.ObjectifiedElement
instead.
from lxml import objectify
class person(objectify.ObjectifiedElement):
def __init__(self, name, age):
super().__init__(name=name, age=str(age))
my_person = person("Paul", 23)
print(etree.tostring(my_person, encoding="unicode"))
# output: <person age="23" name="Paul"/>
Make sure that your class name matches your XML tag, as ObjectifiedElement
uses the class name for the root XML tag name.
As Goyo and bruno desthuilliers pointed out, objectify.Element
is not a class, which means you can't inherit from it. Going off their comments, I used type()
to confirm this for myself and discovered that objectify.Element()
returned an object of type ObjectifiedElement
.
from lxml import objectify
person = objectify.Element("person", name="Paul", age="23")
print(type(person))
# output: <class 'lxml.objectify.ObjectifiedElement'>
After changing my class to inherit from lxml.objectify.ObjectifiedElement
, the class worked as expected.
Upvotes: 2