强制 class 变量初始化的最佳方法

Best way to make class variables initialization mandatory

我有一些 class 变量应该在创建任何 class 实例之前定义。它没有默认值,只有会使用它的人 class 才能初始化它。

我执行以下操作:

class Foo:
    _inited = False  # flag to show if Foo.static_init was called
    _FILE_PATH = None  # this var should be inited before class using

    @classmethod
    def static_init(cls, file_path):
        cls._inited = True
        cls._FILE_PATH = file_path

    def __init__(self):
        if not Foo._inited:
            raise Exception('Foo.static_init should be called first.')

看起来很难看,有什么办法可以更好地进行静态初始化吗?

您似乎拥有的是一种类型 Foo,它取决于值 file_path。但是,我不确定这是否是您想要的,因为您可能有一个 XY problem.

可以使用这样的定义来做到这一点:

def make_Foo(file_path):

    class Foo:
        _FILE_PATH = file_path

        def __init__(self):
            pass

    return Foo

在这种形式下,用户无法在不提供 file_path 参数的情况下获得类型 Foo。要使用这样的 class 可以这样做:

Foo = make_Foo("/var/tmp")
my_foo = Foo()

这还有一个额外的好处,即用户可以通过提供 file_path 的不同值来获得类型 Foo 的多个实例:

Foo2 = make_Foo("/var/log")
Foo3 = make_Foo("/home")

如果你想在项目的任何地方使用实例化版本的Foo,那么你可以在一些共享模块中添加Foo = make_Foo(...)

我决定采用原来的方式,但要使解决方案可重用。这个 class 装饰器可用于强制调用 cls.static_init()

def static_init(cls_to_decorate):
    class Wrapper(cls_to_decorate):
        _static_init_called = False

        def __new__(cls, *args, **kwargs):
            if not Wrapper._static_init_called:
                raise Exception('{}.static_init() should be called.'.format(cls_to_decorate.__name__))
            return super().__new__(cls, *args, **kwargs)

        @classmethod
        def static_init(cls, *args, **kwargs):
            Wrapper._static_init_called = True
            return super().static_init(*args, **kwargs)
    return Wrapper

用法:

@static_init
class Foo:
    _PARAM = None

    def test(self):
        print(Foo._PARAM)

f = Foo()  #Exception: Foo.static_init() should be called.

.

@static_init
class Foo:
    _PARAM = None

    @classmethod
    def static_init(cls, param):
        cls._PARAM = param

    def test(self):
        print(Foo._PARAM)


Foo.static_init('param')
f = Foo()
f.test()  # param