语法上没有。然而,使用装饰器来做到这一点相对容易:
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
Used as:
>>> @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
装饰器忽略未包含在给定列表中的位置和关键字参数:
>>> @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
如果参数可能是“可选的”(即您最多可以指定其中一个关键字参数,但也可以省略所有参数),只需更改!= 1
to <= 1
or in (0,1)
根据你喜欢的。
如果你更换1
有一个数字k
您将装饰器概括为完全接受(或最多接受)k
您提供的集合中的指定参数。
然而,这对 PyCharm 没有任何帮助。据我所知,目前根本不可能告诉 IDE 你想要什么。
上面的装饰器有一个小“bug”:它认为foo=None
就好像您传递了一个值foo
因为它出现在kwargs
列表。通常您会期望传递默认值的行为应该与您根本没有指定参数一样。
正确修复此问题需要检查func
inside wrapper
查找默认值并更改k in keywords
与类似的东西k in keywords and kwargs[k] != defaults[k]
.