Python 3 - проверка атрибута класса без вызова __getattr__

TL;DR: есть ли альтернативы hasattr которые не запускают свойства getter?

Я пишу интерфейс Python для некоторого существующего кода, где я получаю и устанавливаю значения для различных классов фигур и сечений. Из-за большого количества возможных комбинаций я в настоящее время создаю новые классы динамически, подкласс SectionHandler класс и класс формы, такие как Circle, У каждого есть свои методы, которые я хочу сохранить.

Поскольку этот API будет использоваться в сценариях и в интерактивном режиме, я хочу убедиться, что любые атрибуты, измененные после создания экземпляров класса, уже существуют (чтобы новые атрибуты не могли быть созданы из опечаток, и пользователь получает предупреждение при указании несуществующего атрибута).). Поскольку эта проверка должна "пропустить" все новые атрибуты, добавляемые во время подкласса, я предварительно загружаю атрибуты в новый класс, используя словарь атрибутов в type функция (представлена ​​ниже name а также index атрибуты).

я использую hasattr проверить, существует ли атрибут в созданном классе, переопределив __setattr__, как предлагается в этом посте. Это работает, когда атрибут не существует, но проблема в том, когда атрибут существует - так как hasattr кажется, работает, вызывая свойство getter, я получаю посторонние вызовы регистрации от getter, который загрязняет журнал из-за многих модификаций атрибута (особенно для более длинных сообщений).

Есть ли другой способ проверить атрибут класса в динамически генерируемых классах? Я пытался заглянуть в self.__dict__ Вместо того, чтобы использовать hasattr но этот словарь пуст во время создания класса. Какие есть альтернативы?

Я попытался привести минимальный рабочий пример, который отражает структуру, с которой я работаю:

import logging

class CurveBase:
    """Base class for all curves/shapes."""
    def __setattr__(self, attr, value):
        """Restrict to setting of existing attributes only."""
        if hasattr(self, attr):
            return super().__setattr__(attr, value)
        else:
            raise AttributeError(f'{attr} does not exist in {self.__class__.__name__}')


class Circle(CurveBase):
    """Circle-type shape base class."""
    @property
    def diameter(self):
        logging.info(f'Getting {self.name} section {self.index} diameter')
        # diameter = external_getter("Circle_Diameter")
        # return diameter

    @diameter.setter
    def diameter(self, diameter):
        logging.info(f'Setting {self.name} section {self.index} diameter to: {diameter}')
        # external_setter("Circle_Diameter", diameter)


class SectionHandler:
    def __init__(self):
        # Minimal example init
        self.name = 'section_1'
        self.index = 1


if __name__ == '__main__':
    # This is set up by the API code
    logging.basicConfig(level='INFO', format='%(asctime)s - %(levelname)s - %(message)s')
    shape = 'circle'
    attribute_dict = {'name': None, 'index': None}  # Generated based on classes used.
    NewSectionClass = type(f'{shape.capitalize()}Section',
                           (SectionHandler, Circle),
                           attribute_dict)
    section = NewSectionClass()


    # This is an example of API usage
    print(section.diameter)
    # Returns:
    # 2018-12-04 18:53:07,805 - INFO - Getting section_1 section 1 diameter
    # None  # <-- this would be a value from external_getter

    section.diameter = 5
    # Returns:
    # 2018-12-04 18:53:07,805 - INFO - Getting section_1 section 1 diameter  # <-- extra getter call from hasattr()!!!
    # 2018-12-04 18:53:07,805 - INFO - Setting section_1 section 1 diameter to: 5

    section.non_existent
    # Correctly returns:
    # Traceback (most recent call last):
    #   File "scratch_1.py", line 50, in <module>
    #     section.non_existent
    # AttributeError: 'CircleSection' object has no attribute 'non_existent'

1 ответ

TL;DR

Вы должны попробовать позвонить <Object>.__getattribute__() и поймать ошибку

Ваш случай

Что-то вроде этого могло сработать

      class CurveBase:
    """Base class for all curves/shapes."""
    def __setattr__(self, attr, value):
        """Restrict to setting of existing attributes only."""
        try:
            super().__getattribute__(attr)
            return super().__setattr__(attr, value)
        except:
            raise AttributeError(f'{attr} does not exist in {self.__class__.__name__}')

Альтернативы

  • getattr_static() из inspectмодуль, как описано здесь
  • Вы также можете обернуть try catch в функцию bool, как в ответе из пункта 1.

Рекомендации

Другие вопросы по тегам