有没有办法在python中强制互斥函数参数?

Sam*_*son 2 python optional-parameters optional-arguments keyword-argument python-3.x

考虑:

def foobar(*, foo, bar):
    if foo:
        print('foo', end="")
    if bar:
        print('bar', end="")
    if foo and bar:
        print('No bueno', end='')  # I want this to be impossible
    if not foo and not bar:
        print('No bueno', end='')  # I want this to be impossible
    print('')


foobar(foo='bar')  # I want to pass inspection
foobar(bar='foo')  # I want to pass inspection
foobar(foo='bar', bar='foo')  # I want to fail inspection
foobar()  # I want to fail inspection
Run Code Online (Sandbox Code Playgroud)

有没有一种方法可以设置一个函数,使调用它的方法仅在仅传递foo或bar时才通过检查,而无需手动检查函数内部?

Bak*_*riu 5

语法上不可以。但是,使用装饰器执行此操作相对容易:

from functools import wraps

def mutually_exclusive(keyword, *keywords):
    keywords = (keyword,)+keywords
    def wrapper(func):
        @wraps(func)
        def inner(*args, **kwargs):
            if sum(k in keywords for k in kwargs) != 1:
                raise TypeError('You must specify exactly one of {}'.format(', '.join(keywords)))
            return func(*args, **kwargs)
        return inner
    return wrapper
Run Code Online (Sandbox Code Playgroud)

用作:

>>> @mutually_exclusive('foo', 'bar')
... def foobar(*, foo=None, bar=None):
...     print(foo, bar)
... 
>>> foobar(foo=1)
1 None
>>> foobar(bar=1)
None 1
>>> foobar(bar=1, foo=2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in inner
TypeError: You must specify exactly one of foo, bar
>>> foobar()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in inner
TypeError: You must specify exactly one of foo, bar
Run Code Online (Sandbox Code Playgroud)

装饰器忽略给定列表中未包括的位置和关键字参数:

>>> @mutually_exclusive('foo', 'bar')
... def foobar(a,b,c, *, foo=None, bar=None, taz=None):
...     print(a,b,c,foo,bar,taz)
... 
>>> foobar(1,2,3, foo=4, taz=5)
1 2 3 4 None 5
>>> foobar(1,2,3, foo=4, bar=5,taz=6)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in inner
TypeError: You must specify exactly one of foo, bar
Run Code Online (Sandbox Code Playgroud)

如果参数可能是“可选的”(即,您最多可以指定这些关键字参数之一,但也可以省略所有这些参数),则只需更改!= 1<= 1in (0,1)根据需要即可。

如果1用数字替换,k则装饰器将泛化为k从提供的集合中准确(或最多)接受指定的参数。

但是,这无论如何都不会帮助PyCharm。据我所知,目前完全不可能告诉IDE您想要什么。


上面的装饰器有一个小“ bug”:由于它出现在列表中,因此认为foo=None您传递了一个值。通常,您希望传递默认值的行为与完全没有指定参数的行为相同。fookwargs

正确修复,这将需要检查func里面wrapper查找默认值和变化k in keywords的东西像k in keywords and kwargs[k] != defaults[k]

  • @PMende我选择了TypeError,因为通常在您提供错误参数时引发的错误会引发TypeError(例如,def f(a):pass`然后f(b = 1)`会引发TypeError:f()得到因此,我认为`TypeError`在这种情况下更为一致,因为我们正在为该函数模拟特殊的* signature *……但这是一种观点。 (2认同)