`__init__` 装饰器如果为空则处理参数

`__init__` decorator that handles args if empty

假设我有一个 class 这样的:

class Test:
    def __init__(self, a, b):
        self.a = a
        self.b = b

我的目标是创建一个 decorator 来处理填充 init 参数(如果它们不存在),即:

class Test:
    @autoinit
    def __init__(self, a, b):
        self.a = a
        self.b = b

其中 @autoinit 定义如下:

class autoinit:
    def __init__(self, data = {"a": "test_a", "b": "test_b"}):
        self.data = data
    def __call__(self, func):
        decorator = self
        def wrapper(*args, **kwargs):
            print(decorator.data)
            func(self, **decorator.data)
            print(decorator.data)
        return wrapper

因此,它会自动将Test属性分别分配给test_a, test_b

理想的用法是这样的:

test = Test(a="test_z", b="test_x")
test.a == "test_z"
test.b == "test_x"

# however,

test = Test()
test.a == "test_a"
test.b == "test_b"

# but also,

test = Test(a="test_z")
test.a == "test_z"
test.b == "test_b"

我将始终在 Test class 中找到与 data 字典中的键相匹配的参数。

这可能吗?什么是最干净的实现?


更新:

预期用途跨越许多独立的 classes。例如,假设我有一个这样的全局配置:

config = {
    "resourceA": {"a": "test_a", "b": "test_b"},
    "resourceB": {"name": "foo", "value": "bar"}
}

装饰器 @autoinit(resource="resourceA") 的目标是使用 **config[resource] 填充给定 class.

的所有 __init__

我会这样写:

def autoinit(**kwargs):
    if not kwargs:
        kwargs = {"a": "test_a", "b": "test_b"}  # some default

    def wrapper(f):
        def wrapped(*args, **overrides):
            kwargs.update(overrides)  # update kwargs with overrides
            return f(*args, **kwargs)
        return wrapped
    return wrapper

这允许实施 class 如您的问题所述:

class Test:
    @autoinit()
    def __init__(self, a, b):
        self.a = a
        self.b = b

t = Test()
assert t.a = 'test_a'
assert t.b = 'test_b'

t2 = Test(a='test_z')
assert t2.a = 'test_z'
assert t2.b = 'test_b'

综上所述,请考虑使用教你 class 如何从配置本身读取的 mixin。

from abc import ABC
class ConfigurationDefault(ABC):
    @classmethod
    def with_config_defaults(cls, config, **kwargs):
        new_kwargs = {**config, **kwargs}
        return cls(**new_kwargs)

class Test(ConfigurationDefault):
    def __init__(self, a, b):
        self.a = a
        self.b = b

config = {'resources': {'a': 'test_a', 'b': 'test_b'}}
t = Test.with_config_defaults(config['resources'])
t2 = Test.with_config_defaults(config['resources'], a='test_z')