Search code examples
pythonxmlironpythongetattr

Python - How to define attributes not affected by __getattr__?


I'm fairly new to Python. In programming a lot of PHP recently I got used to some creative use of __get and __set "magic" methods. These were only called when a public variable of the class wasn't present.

I'm trying to replicate the same behavior in Python, but seem to be failing miserably. Given there doesn't seem to be a way to actually define class variables in a C++/PHP way, when I try to use variables normally within my class (i.e. via self) it ends up calling __getattr__!

How do I define attributes of my class that I don't want affected by __getattr__?

Some sample code of what I'm trying to do is below, where I'd want self.Document and self.Filename NOT to invoke __getattr__.

Thanks for the help!

class ApplicationSettings(object):
    RootXml = '<?xml version="1.0"?><Settings></Settings>'

    def __init__(self):
        self.Document = XmlDocument()
        self.Document.LoadXml(RootXml)

    def Load(self, filename):
        self.Filename = filename
        self.Document.Load(filename)

    def Save(self, **kwargs):
        # Check if the filename property is present
        if 'filename' in kwargs:
            self.Filename = kwargs['filename']

        self.Document.Save(self.Filename)

    def __getattr__(self, attr):
        return self.Document.Item['Settings'][attr].InnerText

    def __setattr__(self, attr, value):
        if attr in self.Document.Item['Settings']:
            # If the setting is already in the XML tree then simply change its value
            self.Document.Item['Settings'][attr].InnerText = value
        else:
            # Setting is not in the XML tree, create a new element and add it
            element = self.Document.CreateElement(attr)
            element.InnerText = value

            self.Document.Item['Settings'].AppendChild(element)

Solution

  • Apparently if I check for the attribute name in __setattr__ I can then call object's __setattr__ for the attributes I want to use normally. This feels pretty hoaky, but works.

        def __setattr__(self, attr, value):
            # Check for attributes we want to store normally
            if attr == 'Document' or attr == 'Filename':
                object.__setattr__(self, attr, value)
            # If the setting is already in the XML tree then simply change its value
            elif attr in self.Document.Item['Settings']:
                self.Document.Item['Settings'][attr].InnerText = value
            # Setting is not in the XML tree, create a new element and add it
            else:
                element = self.Document.CreateElement(attr)
                element.InnerText = value
    
                self.Document.Item['Settings'].AppendChild(element)