TL;DR:是否有hasattr
不触发属性获取器的替代方法?
我正在为一些现有代码编写 Python 接口,我在其中获取和设置各种形状和截面类的值。由于有大量可能的组合,我目前动态创建新类,子类化一个SectionHandler
类和形状类,例如Circle
. 每个都有我想保留的特定方法。
由于此 API 将在脚本中以交互方式使用,我想确保在类实例化之后修改的任何属性都已经存在(这样就不能从拼写错误中创建新属性,并且在指定不存在的属性时会警告用户)。由于此检查需要“跳过”在子类化期间添加的所有新属性,因此我使用type
函数中的属性字典将属性预加载到新类中(下面由name
andindex
属性表示)。
正如此 SO post中所建议的,我正在hasattr
通过覆盖来检查属性是否存在于创建的类中。这在属性不存在但问题是属性确实存在时有效 - 因为似乎通过调用属性 getter 来工作,所以我从 getter 收到无关的日志记录调用,这会污染许多属性修改的日志(尤其是对于较长的消息)。__setattr__
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'