首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >大多数为类构造函数提供默认值的pythonic方法

大多数为类构造函数提供默认值的pythonic方法
EN

Stack Overflow用户
提问于 2020-03-18 13:58:45
回答 3查看 835关注 0票数 6

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

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

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

我就是这么做的:

代码语言:javascript
复制
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)

问题

  • 在技术上工作的时候,我想知道这是否是最重要的一种方式?
  • 随着我的类的默认值越来越多,代码变得非常重复,我能做些什么来简化它呢?
  • 对我来说,assert似乎也不是最好的选择,它只是一个调试语句,而不是一个验证检查。我在玩弄@property装饰器,在这里我会引发一个异常,以防参数无效,但是对于当前的模式,我希望允许xy在短时间内不是truthy来正确实现优先级(也就是说,我只想在构造函数的末尾检查truthiness。有什么线索吗?
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2020-03-18 14:10:32

通常,如果有多种方法合理地构造对象类型,则可以为备用构造提供S (dict.fromkeys就是一个很好的例子)。请注意,如果用例是有限的,并且是静态定义的,则此方法更适用。

代码语言:javascript
复制
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)  

如果用例中唯一的变化是默认参数,那么每次重写位置参数列表都需要大量开销。相反,我们可以编写一个classmethod来接受用例,并且可选的重写仅作为关键字。

代码语言:javascript
复制
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)

如果您希望从位置上通过这些参数,这将更加困难,但我想这将适合您的需要。

票数 8
EN

Stack Overflow用户

发布于 2020-03-18 14:08:22

Python是一种非常灵活的语言。如果您的代码运行,技术上没有错误的处理方法。然而,如果你想成为"Pythonic",这里有一些提示给你。首先,您不应该使用AssertionError来验证参数的存在或值。如果一个参数没有被传递并且应该在那里,那么您应该引发一个TypeError。如果传递的值不可接受,则应引发ValueError。断言主要用于测试。

当您想要验证参数a中是否存在一个值时,最好执行a is not None,而不是not a。当not aNone0或其他Falsy值对您同样无效时,您可以执行该操作。但是,当目的是检查值的存在时,0None并不相同。

关于您的类,我认为一个更好的方法是在类initalization上展开字典的值。如果从函数签名中删除use_case,并按如下方式调用类:

代码语言:javascript
复制
MyClass(**MY_DEFAULTS["use_case_1"])

Python将打开嵌套字典的值,并将它们作为关键字参数传递给您的__init__方法。如果不希望这些值是可选的,则删除默认值,如果提供的参数与函数签名不匹配,Python将为您引发一个TypeError

如果您仍然希望您的参数不是Falsy,也许您应该为参数的可能值提供一个更具体的范围。如果x的类型是int,并且不需要0值,那么应该将x0进行比较

代码语言:javascript
复制
def __init__(x, y):
    if x == 0 or y == 0:
        raise ValueError("x or y cannot be 0")
票数 2
EN

Stack Overflow用户

发布于 2020-03-18 14:14:36

保留原来的接口,可以使用kwargs读取参数。如果缺少一些,则只在用例匹配的情况下设置默认值。

代码语言:javascript
复制
MY_DEFAULTS = {"use_case_1": {"x": 1, "y": 2},
               "use_case_2": {"x": 4, "y": 3}}

class MyClass:
   def __init__(self, use_case = None, **kwargs):
      for k,v in kwargs.items():
        setattr(self,k,v)
      if use_case:
        for k,v in MY_DEFAULTS[use_case].items():
            if k not in kwargs:
                setattr(self,k,v)
      unassigned = {'x','y'}
      unassigned.difference_update(self.__dict__)
      if unassigned:
        raise TypeError("missing params: {}".format(unassigned))

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

print(MyClass("use_case_1")) # (1, 2)
print(MyClass("use_case_2", y = 10)) # (4, 10)
print(MyClass())

执行此操作:

代码语言:javascript
复制
(1, 2)
(4, 10)
Traceback (most recent call last):
  File "<string>", line 566, in run_nodebug
  File "C:\Users\T0024260\Documents\module1.py", line 22, in <module>
    print(MyClass())
  File "C:\Users\T0024260\Documents\module1.py", line 15, in __init__
    raise TypeError("missing params: {}".format(unassigned))
TypeError: missing params: {'y', 'x'}

随着我的类的默认值越来越多,代码变得非常重复,我能做些什么来简化它呢?

此解决方案允许具有许多参数。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/60741112

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档