您可以动态地将类属性/变量添加到python中的子类吗?

Posted

技术标签:

【中文标题】您可以动态地将类属性/变量添加到python中的子类吗?【英文标题】:Can you dynamically add class attributes/variables to a subclass in python? 【发布时间】:2017-01-19 18:53:38 【问题描述】:

我在 python 中有大量自动生成的类,它们基本上代表通信协议的一部分的枚举,它们看起来像这样

# definitions.py

class StatusCodes(ParamEnum):
    Success = 1
    Error = 2
    UnexpectedAlpaca = 3

class AlpacaType(ParamEnum):
    Fuzzy = 0
    ReallyMean = 1

# etc etc etc

以这种方式定义事物使自动生成器和人类都可以轻松修改。 ParamEnum 类提供所有功能,从传入的网络数据获取/设置、比较、转换和创建等。

但是,这些类需要一些额外的元数据。我确实想将它添加到每个类的源定义中,因为它降低了可读性并且会破坏自动生成器

目前我正在这样做

# param_enum.py

class ParamEnum(object):
    def __init__(self):
        self.__class__._metadata = get_class_metadata(self.__class__)

然而这让我觉得效率有点低,因为每次我们实例化这些枚举之一时都会发生这种情况(这种情况经常发生),而不仅仅是在定义时(元数据不会改变,所以只需要设置一次)

我尝试将其添加到定义文件的底部,但在那里也遇到了问题。

class StatusCodes(ParamEnum):
    Success = 1
    Error = 2
    UnexpectedAlpaca = 3

for var in locals():  # or globals? 
    add_metadata(var)
    #doesn't work because it is in the same file, modifying dict while iteratng

在 python 中是否有一种方法可以在定义类时覆盖/添加功能,可以继承到子类?理想情况下,我想要类似的东西

class ParamEnum(object):
    def __when_class_defined__(cls):
        add_metadata(cls)

【问题讨论】:

在 Python 中,类是其元类的实例。也就是说,您可以通过为您的 ParamEnum 提供一个覆盖 __new__ 的元类来实现此目的。 【参考方案1】:

使用类装饰器

def add_metadata(cls):
    cls._metadata = get_class_metadata(cls)
    return cls

@add_metadata
class StatusCodes(ParamEnum):
    Success = 1
    Error = 2
    UnexpectedAlpaca = 3

您想要做的实际上是拥有装饰器的动机:在创建类或函数之后对其进行修改。

如果你对装饰器不熟悉,请阅读this(虽然有点长)。

您也可以使用元类,但它们比装饰器引入了更多的复杂性。如果你想避免额外的装饰线 @add_metadata 并且你认为它对于指定的子类 ParamEnum 是多余的,你也可以在基类 ParamEnum 中使用惰性计算 _metadata a descriptor。

class MetadataDescriptor(object):
    def __get__(self, obj, cls):
        if cls._metadata_value is None:
            cls._metadata_value = get_class_metadata(cls)
        return cls._metadata_value

class ParamEnum(object):
    _metadata_value = None
    _metadata = MetadataDescriptor()

【讨论】:

我认为描述符比装饰器好。 @AlbertLee 我也认为描述符更优雅。然而,它们是相当高级的“魔法”,这可能是一个障碍,具体取决于维护代码的人。 使用描述符,工作得非常好,并保持我自动生成的文件干净。谢谢!

以上是关于您可以动态地将类属性/变量添加到python中的子类吗?的主要内容,如果未能解决你的问题,请参考以下文章

Python:使用用户输入作为类名的类工厂

如何根据另一个组件的存在有条件地将类添加到 Magnolia 组件模板?

在Angular中动态地将控件添加到FormGroup

如果您从一个视图控制器快速移动到另一个视图控制器,如何动态地将值添加到数组中?

如何在angularjs中动态地将样式属性添加到具有ng-class的元素

无法动态地将小部件添加到 QScrollArea 对象