设计 class 的 Pythonic 方式,它具有可以被 kwargs 覆盖的默认数据成员

Pythonic way to design class which has default data members which can be overridden by kwargs

我认为标题给出了我正在寻找的内容的一般概念,但为了更具体,我将给出一个代码示例。

所以假设我有一个 Python class,其中包含一些必需的位置变量,这些变量也采用任意数量的关键字参数。 class 有很多数据成员,其中一些将由所需的位置变量定义,但大多数是关键字参数变量,程序对这些变量有默认值,但如果用户使用关键字参数,这将覆盖默认值。我正在寻找最“pythonic”的方式来初始化这种类型的 class。关于如何做到这一点,我有两个想法,但每个想法都不令人满意,而且我好像缺少一种更 pythonic 的方式。

#First Option
class SampleOne:
    def __init__(pos1, pos2, **kwargs):
        def do_defaults():
            self.kwarg1 = default_kwarg1
            self.kwarg2 = default_kwarg2
            self.kwarg3 = default_kwarg3

        def do_given():
            for variable, value in kwargs.items():
                self.variable = value
        
        self.pos1 = pos1
        self.pos2 = pos2
        do_defaults()
        do_given()

或者

#Second Option
class SampleTwo:
    def __init__(pos1, pos2, **kwargs):
        self.pos1 = pos1
        self.pos2 = pos2
        self.kwarg1 = kwargs[kwarg1] if kwarg1 in kwargs else default_kwarg1
        self.kwarg2 = kwargs[kwarg2] if kwarg2 in kwargs else default_kwarg2
        self.kwarg3 = kwargs[kwarg3] if kwarg3 in kwargs else default_kwarg3

我不喜欢第一个选项,因为如果要更改一堆默认数据成员,设置一堆默认数据成员似乎很浪费,尤其是在有很多数据成员的情况下。

我不喜欢第二个选项,因为在我看来它看起来过于繁琐且可读性较差 - 我喜欢将默认值与 user-defined 值分开,并认为这会使我的代码更易于理解阅读和改变。

此外,我正在使用 **kwargs 而不是具有默认值的关键字参数,因为我仍处于此代码库开发的早期阶段,因此所需的成员变量可能会发生变化,但也因为正在进行有很多成员变量,拥有所有这些参数会使函数签名变得非常难看。

抱歉,如果我的问题有点 long-winded,这是我第一次在 Whosebug 上提问,我想确保我提供了足够的细节。

此外,如果它有所不同,我的代码需要在 Python 3.8 及更高版本中工作。

这是一种可能的解决方案:

class Solution:
    def __init__(self, pos1, pos2, **kwargs):
        self.pos1 = pos1
        self.pos2 = pos2
        for key, value in kwargs.items():
            if key not in self.__dict__:
                self.__dict__[key] = value
            else:
                raise ValueError(f"{key} already exists in Solution")

您需要注意不要覆盖 class 中的方法或现有变量,所以我让它抛出异常以防您尝试这样做。

用法示例:

>>> solution = Solution(1, 2, kwarg1=1, kwarg2=2, kwarg3=3, kwarg4=4)
>>> print(solution.__dict__)
{'pos1': 1, 'pos2': 2, 'kwarg1': 1, 'kwarg2': 2, 'kwarg3': 3, 'kwarg4': 4}
>>> solution.kwarg4
4

指定您要支持的默认值并采用不需要默认值的任意值。这使您的 类 可读并且实现(相对)清晰:

class Sample:
    def __init__(
        self, 
        pos1, 
        pos2, 
        kwarg1='default_1', 
        kwarg2='default_2', 
        kwarg3='default_3', 
        **kwargs
    ):
        self.pos1 = pos1
        self.pos2 = pos2
        self.kwarg1 = kwarg1
        self.kwarg2 = kwarg2
        self.kwarg3 = kwarg3

        for k, v in kwargs.items():
            setattr(self, k, v)