【问题标题】:Dealing with many parameters/class attributes in Python在 Python 中处理许多参数/类属性
【发布时间】:2020-07-27 07:37:07
【问题描述】:

我正在创建一个 Python 类,它将使用它的参数在该类中定义另外两个类。例如,ClassA 包含 ClassBClassC。正因为如此,ClassA的参数很多。

我目前正在使用**kwargs 解决这个问题,将classBclassC 参数用于classA 参数,并仅保存classA 参数的输入参数。例如:

class A:
    def __init__(self, a1, a2, a3, **kwargs):
        self.a1 = a1
        self.a2 = a2
        self.a3 = a3

        # Get B and C kwargs
        default_params = {
            'b1': kwargs.get('b1', 1),
            'b2': kwargs.get('b1', 2),
            'c1': kwargs.get('c1', 1),
            'c2': kwargs.get('c1', 2),          
        }
        for param in default_params:
            self.__dict__[param] = default_params[param]

        B = classB(a1=self.a1, b1=self.b1, b2=self.b2)
        C = classC(a2=self.a2, c1=self.c1, c2=self.c2)

与下面的相比,这是一个更糟糕的解决方案吗?

class A:
    def __init__(
        self,
        a1, a2, a3,
        b1, b2, c1, c2):
        self.a1 = a1
        self.a2 = a2
        self.a3 = a3
        self.b1 = b1
        self.b2 = b2
        self.c1 = c1
        self.c2 = c2

我担心的是,如果 B 类和 C 类的参数非常多,那么 A 类的属性似乎太多了。另一方面,无论如何,第一种方法似乎占用了更多的行。

另一种方法是,如果我只将 kwargs 传递给 classBclassC,但是当其中一个类中不存在某些参数时,这会出错。

对于具有许多属性并利用这种设计模式的类,我在这里看不到 pythonic 方式吗?

【问题讨论】:

  • 这个设计的某些部分看起来很奇怪,你能澄清一下吗?当其余代码显式使用b1b2c1c2 参数(间接作为键)时,为什么要使用**kwargs?当您只想传递参数时,为什么要存储参数(self.b1,...)?
  • 请注意,风格问题可能更适合CodeReview。请务必查看他们的How To Ask 页面以提供所有相关信息。
  • 这是个好问题。例如,对于使用**kwargs*, I was thinking if a user gives a specific value for b1`,我可以从kwargs 获取它,而无需定义一长串属性。对于第二个问题,我打算将参数记录在一个单独的文件中,尽管您是对的,但我认为我实际上不需要将其存储为 self.b1
  • 我之前尝试过 codereview,但它们需要我无法提供的真实项目代码。我在这里创建了一个最小的可重现示例,它在 SO 中被更广泛地接受。

标签: python oop design-patterns


【解决方案1】:

您的主要标准应该是避免模仿 Python 本身提供的东西——即参数和默认值,以及实例属性。

由于__init__ 已经知道它期望的所有属性和默认值(通过default_params),所以直接将它们添加到签名中。避免在A 上存储classBclassC 的参数——如果需要,从实例中获取它们。

class A:
    # handle parameters/defaults natively
    def __init__(self, a1, a2, a3, b1=1, b2=2, c1=1, c2=2):
        self.a1 = a1
        self.a2 = a2
        self.a3 = a3
        # do not store B/C attributes, store B/C directly
        self.b = classB(a1=self.a1, b1=b1, b2=b2)
        self.c = classC(a2=self.a2, c1=c1, c2=c2)

这在功能上等同于第一种情况(减去公开所有参数),但使用的是原生 Python 功能。


如果A 必须提供额外的功能,添加他们明确:

  • 如果A 应该采用(并忽略)附加参数,则附加包罗万象的**kwargs

    class A:
        # discard additional parameters/defaults explicitly
        def __init__(self, a1, a2, a3, b1=1, b2=2, c1=1, c2=2, **_):
            ...
    
  • 如果A 应该公开classB 和/或classC 字段,请从实例中提供它们。

    class A:
        ...
    
        @property
        def b1(self):
            return self.b.b1
    

【讨论】:

  • 这非常有用且内容丰富,谢谢!你能指出我应该去哪里学习这些有用的代码样式吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-09
  • 2017-06-08
  • 2014-10-21
  • 2020-07-06
  • 1970-01-01
  • 2015-09-18
相关资源
最近更新 更多