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.
Рекомендации
- Python Документациядля
__getattribute__
- Некоторые подсказки из последнего раздела этого ответа