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'