【问题标题】:Why define constants in a metaclass?为什么要在元类中定义常量?
【发布时间】:2022-11-16 22:13:59
【问题描述】:

我最近继承了一些代码。它有一个名为 SystemConfig 的类,它充当在整个代码库中使用的常量的抓包。但是,虽然一些常量是直接在该类上定义的,但其中一大堆常量被定义为该类的元类的属性。像这样:

class _MetaSystemConfig(type):
    @property
    define CONSTANT_1(cls):
        return "value 1"

    @property
    define CONSTANT_2(cls):
        return "value 2"

    ...

class SystemConfig(metaclass=_MetaSystemConfig):
    CONSTANT_3 = "value 3"
    ...

该类从不实例化;这些值仅用作 SystemConfig.CONSTANT_1 等。

仍然参与该项目的人似乎都不知道为什么要这样做,除了有人似乎认为这样做的人认为这样做使单元测试更容易。

有人可以向我解释这样做的任何优点以及为什么我不应该将所有属性移动到 SystemConfig 类并删除元类吗?

编辑添加:元类定义不包含除属性以外的任何内容。

【问题讨论】:

  • 这真的很奇怪。可能有这样做的理由,但可能没有好的原因。也许有人想强制只读?不过,我们只能推测。
  • 可能没有办法回答这个不基于意见的问题。 @user2357112 有一个好主意,他们可能真的很想强制执行它的“常量”。另一种可能性是它打算用作混合类,但它应该只是子类中的普通基类,而不是元类。
  • 这就是为什么,虽然我们都讨厌这样做,但它批判的写怪异的文档.以前的开发人员可能认为这是“显而易见的”。可能没有任何充分的理由,但我们永远不会知道。
  • @user2357112 - 但是_MetaSystemConfig.CONSTANT_1 = "new value"真的没有比SystemConfig.CONSTANT_1 = "new value"难写多少,而且同样有效......
  • 是的,这看起来被一些人严重过度设计了,他们认为在类装饰器就足够的时候使用元类会很聪明。

标签: python python-3.x pytest python-unittest pytest-mock


【解决方案1】:

所以我弄清楚了为什么这样做。这些属性被定义为属性,因为它们中的许多相互依赖 - 一个用于目录,另一个用于该目录的子目录,几个用于分布在目录中的文件等等。

但是@property 不适用于类方法。 Python 3.9 修复了@classmethod,以便它可以堆叠在@property 之上,但这在 Python 3.11 中再次被删除。因此,作为一种解决方法,他将这些属性放在元类中(大概是在看到this question 之后)。

然而,实现一个适用于类方法的属性装饰器并不完全是火箭科学,所以为了后来者的利益并且必须弄清楚发生了什么,我已经用 SystemConfig 类上的类属性替换了元类属性.对于任何试图解决这个问题的人来说,这可以作为一个装饰器:

class class_property:
    def __init__(self, _g):
        self._g = _g

    def __get__(_, _, cls):
        return self._g(cls)

实现 setter 似乎要困难得多,因为在分配给类变量时不使用 __set__。但我不需要它。

【讨论】:

    【解决方案2】:

    将一组常量添加到类中可以使用简单的装饰器来完成,无需属性。

    def add_constants(cls):
        cls.CONSTANT_1 = "value 1"
        cls.CONSTANT_2 = "value 2"
    
    
    @add_constants
    class SystemConfig:
        CONSTANT_3 = "value 3"
    

    我不担心用户通过显式地为任何“常量”分配一个新值而搬起石头砸自己的脚,所以我认为跳过箍只是为了添加只读类属性,这比它的价值更麻烦。

    元类的问题在于它们无法组合。如果C1使用元类M1C2使用元类M2,你不能假设class C3(C1, C2): ...会工作,因为这两个元类可能不兼容。您引入越多的元类来完成您可以在没有元类的情况下完成的事情,就会出现越多的此类问题。当您别无选择时使用元类,不仅仅是因为您认为它是继承或装饰器的更酷的替代品。

    【讨论】:

      猜你喜欢
      • 2019-11-12
      • 1970-01-01
      • 2021-12-08
      • 2015-07-01
      • 1970-01-01
      • 2019-07-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多