Python 3 - проверка атрибута класса без вызова __getattr__ - PullRequest
0 голосов
/ 04 декабря 2018

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

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

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

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

Есть ли другой способ проверить атрибут класса в динамически генерируемых классах?Я пытался искать в 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'
...