可选参数,要求某些组合必填

2 投票
3 回答
771 浏览
提问于 2025-04-18 11:21

我有一个一般性的问题,还有一个具体的使用场景。

可选参数其实挺简单的,比如你可以这样写: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中的三个。但我的使用场景并不需要这么复杂。

对于def func(a, b, c=None, d=None): ...,我想要的情况是:c和d中必须提供其中一个,不能两个都不提供,也不能两个都提供。

我想到的解决方案包括:
- 在函数内部手动检查c和d有多少个不是None,如果不是正好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

这些方法都可以,但有什么标准的、被广泛接受的、符合Python风格的做法呢?

3 个回答

1

这里还有一个例子,它允许你指定参数,并且区分了 c=Nonec 没有给出这两种情况,同时还明确提供了参数名称:

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'")
2

你可以直接使用关键字参数字典:

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
4

我觉得在你这个简单的例子中,最简单的方法就是把代码拆分成几个不同的函数。每个函数负责不同的工作,就像你最后提到的那种情况。

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 FowlerStack Overflow 上的讨论。这些讨论通常提到布尔参数,但实际上它们的意思是根据某个参数走不同的代码路径,而这个参数本身没有其他影响。

另一个可以查找的词是“控制耦合”。

撰写回答