在Python中使用参数化Mixin时如何保留信息丰富的`__init__`签名?

问题描述 投票:0回答:1

在我的 Python 项目中,我大量使用 Mixins 作为设计模式,并且我想继续这样做。但是,我在最后一堂课中遇到了

__init__
方法签名的问题。由于我通过
**kwargs
传递参数,因此生成的签名对于内省、文档或类型检查没有帮助。下面举个例子来说明这个问题:

class Base:
    def __init__(self, arg1):
        self.arg1 = arg1

class ParamMixin1:
    def __init__(self, arg2, **kwargs):
        super().__init__(**kwargs)
        self.arg2 = arg2

class ParamMixin2:
    def __init__(self, arg3, **kwargs):
        super().__init__(**kwargs)
        self.arg3 = arg3

class NonParamMixin:
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

class Derived(NonParamMixin, ParamMixin1, ParamMixin2, Base):
    pass

d = Derived(arg1=1, arg2=2, arg3=3)

from inspect import signature
print(signature(Derived.__init__))

打印:

(self, **kwargs)

签名不是很有帮助,因为所有参数都隐藏在

**kwargs
下。我可以从技术上重写像这样的
__init__
方法来公开完整的签名:

class Base:
    def __init__(self, arg1):
        self.arg1 = arg1

class ParamMixin1:
    def __init__(self, arg2, arg1):
        super().__init__(arg1)
        self.arg2 = arg2

class ParamMixin2:
    def __init__(self, arg3, arg2, arg1):
        super().__init__(arg2, arg1)
        self.arg3 = arg3

class NonParamMixin:
    def __init__(self, arg3, arg2, arg1):
        super().__init__(arg3, arg2, arg1)

class Derived(NonParamMixin, ParamMixin1, ParamMixin2, Base):
    pass

from inspect import signature
print(signature(Derived.__init__))

这是可行的,并且签名提供了更多信息,但它引入了很多样板文件并且需要参数的正确顺序,这有时并不重要,具体取决于所使用的 mixin。此外,有时只使用 mixins 的一个子集。

我尝试创建一个元类来动态覆盖

__init__
签名,但它变得非常混乱,而且我无法让它可靠地工作。

但是,无法访问正确的

__init__
签名感觉是违反直觉的。如果没有它,我需要手动跟踪所有 mixin 及其所需的参数,这似乎不切实际。当然,有更好的方法来管理这个问题吗?

任何实现更清洁、更易于维护的解决方案的建议或替代方法也将不胜感激!

python oop mixins signature
1个回答
0
投票
from dataclasses import dataclass
from inspect import signature


@dataclass(kw_only=True)
class Base:
    arg1: int


@dataclass(kw_only=True)
class ParamMixin1:
    arg2: int


@dataclass(kw_only=True)
class ParamMixin2:
    arg3: int


@dataclass
class NonParamMixin:
    pass


@dataclass
class Derived(NonParamMixin, ParamMixin1, ParamMixin2, Base):
    pass


d = Derived(arg1=1, arg2=2, arg3=3)
# d = Derived(1, 2, 3)  # raises an error because of kw_only=True

print(signature(Derived.__init__))
# (self, *, arg1: int, arg3: int, arg2: int) -> None
# * means that all the arguments after * are keyword-only arguments
# This way you don't have to worry about order
© www.soinside.com 2019 - 2024. All rights reserved.