【问题标题】:Optional Parameters, certain combination of them required可选参数,需要它们的某些组合
【发布时间】:2014-06-27 17:40:58
【问题描述】:

我有一个一般性问题以及一个特定用例。

可选参数很简单:def func(a, b, c=None): ...,然后在正文中可能使用 c 的任何地方,只要先写 if c:,或者类似的东西。但是当需要某种参数组合时呢?一般情况是考虑存在或不存在精确参数的任意情况。对于函数def func(a, b, c=None, d=None, e=None, f=None): ...,这将包括一些愚蠢的事情,例如:提供 c 和 d 但不提供 e 和 f,或仅提供 e,或提供 c、d、e 和 f 中的至少 3 个。但我的用例不需要这样的通用性。

对于 def func(a, b, c=None, d=None): ...,我希望提供 c 和 d 中的一个。

我想到的解决方案包括:
- 在正文中,手动检查有多少 c 和 d 不是 None,如果不完全是 1,则返回错误,说明需要指定正好 1
例如。

def func(a, b, c=None, d=None):
    how_many_provided = len([arg for arg in [c, d] if arg]) # count the non-None optional args
    if not how_many_provided == 1:
        return "Hey, provide exactly 1 of 'c' and 'd'"
    if c:
        # stuff to do if c is provided
    elif d:
        # stuff to do if d is provided

- 将函数更改为def func(a, b, e, f): ...,其中 e 代表 c 或 d,f 表示其中 e 代表哪一个。
前任。

def func(a, b, e, f):
    if f == 'c':
        # stuff to do if c is provided, with e as c
    if f == 'd':
        # stuff to do if d is provided, with e as d

这些会起作用,但是标准/公认/pythonic 的方法是什么?

【问题讨论】:

  • 调用函数的用户如何知道哪些组合是可接受的?
  • 我第二个@Mark 的问题。相反,您应该编写一个接受a, b, cd=None 的函数定义并测试cd 以查看它是c 还是d,然后采取相应措施。
  • FYI 在你的第一个例子中,说sum(arg is not None for arg in [c,d]) 会更好看,但是 YMMV
  • 这是python 3吗?仅使用关键字 args 可以使答案更好一些。
  • 可接受的组合将被记录(在我的例子中,在函数中的文档字符串中,但对于其他情况,可能在外部文档中)。除了阅读函数本身之外,用户不总是这样知道使用函数的细节吗?

标签: python optional-parameters


【解决方案1】:

您可以只使用关键字 args dict:

def func(a, b, **kwargs):
  valid_args = len(kwargs) == 1 and ('c' in kwargs or 'd' in kwargs)

  if not valid_args:
      return "Hey, provide exactly 1 of 'c' and 'd'"
  if 'c' in kwargs:
      # stuff to do if c is provided
  elif 'd' in kwargs:
      # stuff to do if d is provided

【讨论】:

  • 是的,但这不适用于 IDE 和自省等
  • 我会被类似的东西诱惑:valid_args = len({'c', 'd'}.insection(kwargs)) == 1
【解决方案2】:

我想说,在您的简单案例中,对您的用户来说最简单的方法是重构为单独的函数。每个功能都按照描述的方式完成不同的工作,然后是共同的工作,例如最后一个案例

def funcC(a, b, c):
        # stuff to do if c is provided, with e as c
    common_func(a,b,c, None)

def funcD(a, b, d):
    # stuff to do if d is provided, with e as d
    common_func(a,b,None, d)

然后用户知道哪些参数很重要,并且只能使用有效的可能组合,用户不必猜测或有机会错误地调用它们。您提供函数可以为调用者不提供的参数提供所需的任何内容。

通过谷歌搜索“标志参数”可以找到更长的解释,例如Martin Fowler Stack Overflow 这些往往会提到布尔参数,但这实际上是同一件事,不同的代码路径取决于没有其他影响的参数。

要寻找的另一个短语是“控制耦合”

【讨论】:

  • 我总是喜欢编写一个包罗万象的函数,这样用户就不必关心哪些参数很重要。例如。 def func(a, b, **kwargs): if 'c' in kwargs and 'd' in kwargs: throwException(); if 'c' in kwargs: _funcC(a,b,kwargs['c']) elif 'd' in kwargs: _funcD(a,b,kwargs['d'])
  • @AdamSmith 问题是调用者必须关心,因为他必须选择不提供参数,即在问题“我希望提供 c 和 d 中的一个。”
  • 我想我无法想象 OP 会要求没有两个 cd 被调用的用例,而且对用户来说并不明显。 IE。一个可以从元组或字符串中获取信息的类构造函数,其中之一应该是显而易见的。
  • @AdamSmith:当函数接受各种参数时,单元测试变得更加困难。您必须测试每一种有效的输入形式,以及每一种无效的输入组合。当您使用单独的函数时,单元测试仍然很简单。当代码库变得有点复杂时,这可能是一个很大的优势。
【解决方案3】:

这是另一个,它允许指定参数,并区分 c=Nonec not given,同时仍显式提供参数名称:

undefined = object()

def func(a, b, c=undefined, d=undefined):
    if (c is undefined) ^ (d is undefined):
        raise TypeError("Hey, provide exactly 1 of 'c' and 'd'")

    ...

在 Python 3 上,仅关键字参数使它变得更好,确保调用者明确指定 cd

def func(a, b, *, c=undefined, d=undefined):
    if (c is undefined) ^ (d is undefined):
        raise TypeError("Hey, provide exactly 1 of 'c' and 'd'")

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-29
    • 2010-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-03
    • 1970-01-01
    相关资源
    最近更新 更多