1

TL;DR:是否有hasattr不触发属性获取器的替代方法?

我正在为一些现有代码编写 Python 接口,我在其中获取和设置各种形状和截面类的值。由于有大量可能的组合,我目前动态创建新类,子类化一个SectionHandler类和形状类,例如Circle. 每个都有我想保留的特定方法。

由于此 API 将在脚本中以交互方式使用,我想确保在类实例化之后修改的任何属性都已经存在(这样就不能从拼写错误中创建新属性,并且在指定不存在的属性时会警告用户)。由于此检查需要“跳过”在子类化期间添加的所有新属性,因此我使用type函数中的属性字典将属性预加载到新类中(下面由nameandindex属性表示)。

正如此 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'
4

1 回答 1

0

TL;博士

您必须尝试调用<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描述的模块
  • 您还可以将__getattribute__try catch 包装到 bool 函数中,如第 1 点的答案。

参考

于 2021-06-11T14:03:59.180 回答