为类构造函数提供默认值的最pythonic方式

tho*_*hal 5 python

我试图坚持谷歌的风格指南,从一开始就努力保持一致性。

我目前正在创建一个模块,在这个模块中我有一个类。我想为不同的标准用例提供一些合理的默认值。但是,我想让用户灵活地覆盖任何默认值。我目前正在做的是提供一个具有默认值(针对不同用例)的模块范围的“常量”字典,并且在我的类中,我在构造函数中提供参数优先于默认值。

最后,我想确保我们以参数的有效值结束。

这就是我所做的:

MY_DEFAULTS = {"use_case_1": {"x": 1, "y": 2},
               "use_case_2": {"x": 4, "y": 3}}

class MyClass:
   def __init__(self, use_case = None, x = None, y = None):
      self.x = x
      self.y = y
      if use_case:
         if not self.x:
            self.x = MY_DEFAULTS[use_case]["x"]
         if not self.y:
            self.y = MY_DEFAULTS[use_case]["y"]
      assert self.x, "no valid values for 'x' provided"
      assert self.y, "no valid values for 'y' provided"
   def __str__(self):
      return "(%s, %s)" % (self.x, self.y)  

print(MyClass()) # AssertionError: no valid values for 'x' provided
print(MyClass("use_case_1")) # (1, 2)
print(MyClass("use_case_2", y = 10) # (4, 10)
Run Code Online (Sandbox Code Playgroud)

问题

  • 在技​​术上工作时,我想知道这是否是最 Pythonic 的方法?
  • 随着我的类的默认值越来越多,代码变得非常重复,我可以做些什么来简化它?
  • assert对我来说似乎也不是最好的选择,而是调试语句而不是验证检查。我正在玩弄@property 装饰器,如果存在无效参数,我会在那里引发异常,但是对于当前的模式,我想允许x并且y在短时间内不要truthy正确实现优先级(也就是说我只想检查将truthiness年底的构造的。对任何提示?

Tad*_*sen 7

一般来说,如果有不止一种方法可以合理地构造您的对象类型,您可以为替代构造提供classmethods(这dict.fromkeys是一个很好的例子)。请注意,如果您的用例有限且静态定义良好,则此方法更适用。

class MyClass:
   def __init__(self, x, y):
      self.x = x
      self.y = y
   @classmethod
   def make_use_case1(cls, x=1, y=2):
       return cls(x,y)
   @classmethod
   def make_use_case2(cls, x=4, y=3):
       return cls(x,y)

   def __str__(self):
      return "(%s, %s)" % (self.x, self.y)  
Run Code Online (Sandbox Code Playgroud)

如果用例中唯一的变化是默认参数,那么每次重新编写位置参数列表会带来很大的开销。相反,我们可以编写一个classmethod以仅将用例和可选覆盖作为关键字。

class MyClass:
    DEFAULTS_PER_USE_CASE = {
        "use_case_1": {"x": 1, "y": 2},
        "use_case_2": {"x": 4, "y": 3}
    }
    @classmethod
    def make_from_use_case(cls, usecase, **overrides):
        args = {**cls.DEFAULTS_PER_USE_CASE[usecase], **overrides}
        return cls(**args)

    def __init__(self, x,y):
        self.x = x
        self.y = y

    def __str__(self):
        return "(%s, %s)" % (self.x, self.y)

x = MyClass.make_from_use_case("use_case_1", x=5)
print(x)
Run Code Online (Sandbox Code Playgroud)

如果您希望按位置传递参数会更困难,但我想这会满足您的需求。