Python-как определить атрибуты, не затронутые getattr?


Я довольно новичок в Python. В последнее время в программировании многих PHP я привык к некоторому творческому использованию __get и __set "магических" методов. Они вызывались только тогда, когда открытая переменная класса отсутствовала.

Я пытаюсь повторить то же самое поведение в Python,но, похоже, терплю неудачу. Учитывая, что на самом деле нет способа определить переменные класса в C++/PHP, когда я пытаюсь использовать переменные обычно в моем классе (т. е. через self), это заканчивается вызовом __getattr__!

Как я могу определить атрибуты моего класса, на которые я не хочу влиять __getattr__?

Ниже приведен пример кода того, что я пытаюсь сделать, где я хотел бы self.Document и self.Filename не вызывать __getattr__.

Спасибо за помощь!

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

4 ответа:

__getattr__ вызывается только тогда, когда Python не может найти атрибут в самом экземпляре или в любом из его базовых классов. Простое решение состоит в том, чтобы добавить Document и Filename в класс, чтобы он был найден.

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

То, что вам действительно нужно здесь, - это дескриптор . Подсоединение __getattr__ и __setattr__ подобным образом на самом деле не рекомендуется.

Я бы использовал свойства . Использование декоратора @property делает его еще красивее.

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x
Затем вы можете получить доступ к C.x, и он автоматически вызовет геттер для x, а также автоматически вызовет сеттер для x, когда вы назначаете C.x.

Очевидно, если я проверяю имя атрибута в __setattr__, я могу вызвать объект __setattr__ для атрибутов, которые я хочу использовать нормально. Это кажется довольно странным, но работает.

    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)