对 config.py 使用 class 继承可能有哪些缺点?

What would be the possible drawbacks of using class inheritance for config.py?

我一直在尝试想出在 Python 项目中处理配置的最佳方法,我决定继续创建 config.py 并将所有配置作为 classes.

然而,我发现:

  1. 如果您想要多个配置,最好有某种形式的“基本”配置。
  2. 有时在 dict 中获取配置在您必须将其放入某些函数参数时会派上用场。

所以我创建了我自己的方式来处理config.py的配置:

  1. 在我创建任何新配置时继承 BaseConfig NewConfig
  2. 继承 @classmethod 即 returns 所有 class 变量作为 dict

但是,由于我是从头开始创建这段代码的,所以我想知道是否有任何可能的副作用,以后会让我后悔。

我不确定的一件事是继承 class 个变量是否是个好主意。

此外,我设法使 get_dict() 工作,但我不知道它的机制是否良好且可扩展。

下面是我的代码:

class BaseConfig:
    ROOT_DIR = Path('.').resolve()
    
    print_every = 2000
    epochs = 3
    lr = 1e-5
    batch_size = 1
    loss = "CrossEntropyLoss"
    optim = "SGD"

    @classmethod
    def get_dict(cls):
        if cls is BaseConfig:
            cls_dict = {k:v for k, v in cls.__dict__.items() if not k.startswith('_') and k != "get_dict"}
        else:
            cls_dict = BaseConfig.get_dict()
            cls_dict.update({k:v for k, v in cls.__dict__.items() if not k.startswith('_') and k != "get_dict"})
        
        return cls_dict
    

class NewConfig(BaseConfig):
    epochs = 2
    lr = 5e-5
    optim = "Adam"
    dataset = "CIFAR10"

我已经为我自己的一些项目做过这样的配置,我认为它工作得相当好。在大多数情况下,我认为继承 class 属性不是问题。

我能想到的一些缺点包括:

  • 重用 parent class 中的 class 属性可能会有点尴尬,因为你无法说“parent class's 属性”来自 child class 而没有明确说明 parent。例如,如果您希望您的配置属性之一是 child classes 可以添加到的列表,您要么需要这样的东西:

    class A:
         my_property = ["a"]
    
     class B(A):
         my_property = A.my_property + ["b"]
    

    或者这个:

    class A:
         my_property = ["a"]
    
    class B(A):
         pass
    
    B.my_property = B.my_property + ["b"]
    

    我觉得两者都不是特别好,但这对你来说可能不是什么大问题。

  • 这种配置需要用户了解一些基本的Python。 (当然,根据应用程序,这可能不是问题。)

  • 对于某些用户,包括那些熟悉 Python 的用户,这种配置似乎有悖常理。我知道有些人觉得必须编写 Python 代码作为配置很奇怪,甚至更奇怪的是将配置变成 classes.


关于 get_dict 实现——您可以使用 dir 来简化它。例如,

@classmethod
def get_dict(cls):
    return {k:getattr(cls,k) for k in dir(cls) if not k.startswith("_") and k != "get_dict"}

这应该适用于您 BaseConfig 的任何后代。

但是,这种方法有一个问题。如果您想向任何 class 中添加更多方法或 class 方法,则需要相应地更新 get_dict;这是一个痛苦,是 error-prone。对于省略方法,您可以尝试这样的方法。

@classmethod
def get_dict(cls):
    return {k:getattr(cls,k) for k in dir(cls) if not k.startswith("_") and not callable(getattr(cls,k))}

我认为除非您的某些配置参数是函数,否则这应该能正常工作。在这种情况下,您可能不得不求助于维护应由 get_dict.

返回的参数的白名单