【问题标题】:Python 3 - Check class attribute without calling __getattr__Python 3 - 在不调用 __getattr__ 的情况下检查类属性
【发布时间】:2019-05-05 18:23:45
【问题描述】:

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

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

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

我正在使用hasattr 通过覆盖__setattr__ 来检查属性是否存在于创建的类中,正如this SO post 中所建议的那样。这在属性不存在但问题是当属性确实存在时有效 - 因为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'

【问题讨论】:

    标签: python python-3.x properties attributes hasattr


    【解决方案1】:

    TL;DR

    您必须尝试调用&lt;Object&gt;.__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 模块,如here 所述
    • 您也可以将__getattribute__ try catch 包装成一个布尔函数,就像第 1 点的答案一样。

    参考文献

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-04-06
      • 2013-08-17
      • 2021-09-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多