Python 需要两个关键字参数中的一个

Python 需要两个关键字参数中的一个,python,Python,我有一个可以接受关键字参数的函数,我需要其中一个。比如说美元和欧元,我只想要一个。现在,我正在这样做(举例),但我发现它相当复杂。还有其他更好的办法吗 def set_value(country, **kargs): if len(kargs) == 1: if kargs.keys()[0] == 'dollar': pass # do something elif kargs.keys()[0] == 'euro':

我有一个可以接受关键字参数的函数,我需要其中一个。比如说美元和欧元,我只想要一个。现在,我正在这样做(举例),但我发现它相当复杂。还有其他更好的办法吗

def set_value(country, **kargs):

    if len(kargs) == 1:
        if kargs.keys()[0] == 'dollar':
            pass # do something
        elif kargs.keys()[0] == 'euro':
            pass # do something
        else:
            raise ValueError('One keyword argument is required: dollar=x or euro=x')
    else:
        raise ValueError('One keyword argument is required: dollar=x or euro=x')

谢谢

您可以对以下对象使用设置操作:

在Python3中,使用
kargs.keys()

集合操作不同结果的演示:

>>> kargs = {'dollar': 1, 'euro': 3, 'foo': 'bar'}
>>> kargs.viewkeys() & {'dollar', 'euro'}
set(['dollar', 'euro'])
>>> del kargs['euro']
>>> kargs.viewkeys() & {'dollar', 'euro'}
set(['dollar'])
>>> del kargs['dollar']
>>> kargs.viewkeys() & {'dollar', 'euro'}
set([])
换句话说,
&
集合交集为您提供两个集合中存在的所有关键点的集合;在字典和显式集合文字中。只有当一个且仅有一个命名关键点存在时,交点的长度才会为1

如果您不想允许除
dollar
euro
之外的任何其他关键字参数,那么您也可以使用适当的子集测试。仅当左侧集合严格小于右侧集合时,才对两个集合使用
>>kargs={'dollar':1,'euro':3,'foo':'bar'}
>>>{}.viewkeys()>>德尔卡格斯['foo']
>>>{}.viewkeys()>>德尔卡格斯[美元]
>>>{}.viewkeys()>>德尔卡格斯[欧元]
>>>{}.viewkeys()

请注意,现在不再接受
'foo'
键。

我们不要使用
kargs.keys()[0]
,因为这取决于键的顺序,未指定。它现在可以工作了,但很脆弱,因为如果添加另一个关键字参数或迁移到Python3,它就会崩溃

请注意,您必须使用

通常,我会尽量避免使用
**kwargs
,除非我将关键字参数传递给另一个函数。如果您使用
**kwargs
,那么您可以调用
set_value(dolar=5)
,并且永远不会注意到您拼错了“dollar”

还要注意,要引发的正确异常是
TypeError

然而,如果你预计货币范围会扩大

CURRENCIES = {'euro', 'dollar', 'quatloo', 'zorkmid'}
def set_value(country, **kwargs):
    if len(kwargs) != 1 or not CURRENCIES.issuperset(kwargs.keys()):
        raise TypeError('exactly one supported currency must be specified')
但是,我可能不会使用单独的关键字参数:

Value = collections.namedtuple('Value', 'currency amount')
def set_value(country, value):
    ...

set_value(country, Value('USD', Decimal('15.30'))

您可以使用
dict.pop

def set_value(country, **kwargs):
    euros = kwargs.pop('euro', None)
    dollars = kwargs.pop('dollar', None)
    if (euros is None is dollars) or (euros is not None is not dollars):
        raise ValueError('One keyword argument is required: dollar=x or euro=x')
    elif euros:
        #do something
    else:
        #do something

因为len(kargs)==1,所以顺序并不重要,因为只存在一个关键字。当添加另一个关键字参数时会发生什么?迁移到Python 3时会发生什么情况?我说的是为函数添加另一个参数,不是为函数调用添加另一个参数。当然,这个问题的“正确”答案完全是主观的,但我更喜欢这种方法。如果您知道所有可能的参数(并且只有两个),为什么不想在函数参数列表中列出它们呢?做任何其他事情都会让阅读函数的用户更难确定函数的实际工作方式。我真的不想阅读函数的整个源代码来确定它接受哪些参数。在阅读了答案后,我感觉就像@MarkHildreth所说的,不使用关键字参数可能更好。但是既然我是这样问的,我就接受Martijn Pieters的回答。
set()
检查
kwargs.viewkeys()
是否是一个正确的非空子集,即
{'euro'}
{'dollar'}
。这不会在调用站点检测到类似
set\u值(dolar 5,euro=6)这样的错误
。Python通常会检测到这些错误并引发
TypeException
@DietrichEpp:如果OP想要支持额外的关键字参数怎么办?@MartijnPieters:我说的是拼写错误的关键字参数。@DietrichEpp:是的,我理解;但接受带有“
**kwargs
一网打尽”的任意关键字参数,永远也无法防止拼写错误。
>>> kargs = {'dollar': 1, 'euro': 3, 'foo': 'bar'}
>>> {}.viewkeys() < kargs.viewkeys() < {'dollar', 'euro'}
False
>>> del kargs['foo']
>>> {}.viewkeys() < kargs.viewkeys() < {'dollar', 'euro'}
False
>>> del kargs['dollar']
>>> {}.viewkeys() < kargs.viewkeys() < {'dollar', 'euro'}
True
>>> del kargs['euro']
>>> {}.viewkeys() < kargs.viewkeys() < {'dollar', 'euro'}
False
def set_value(country, dollar=None, euro=None):
    if dollar is None and euro is None:
        raise TypeError('Need dollar or euro argument')
    if dollar is not None and euro is not None:
        raise TypeError('Cannot have both dollar and euro argument')
CURRENCIES = {'euro', 'dollar', 'quatloo', 'zorkmid'}
def set_value(country, **kwargs):
    if len(kwargs) != 1 or not CURRENCIES.issuperset(kwargs.keys()):
        raise TypeError('exactly one supported currency must be specified')
Value = collections.namedtuple('Value', 'currency amount')
def set_value(country, value):
    ...

set_value(country, Value('USD', Decimal('15.30'))
def set_value(country, **kwargs):
    euros = kwargs.pop('euro', None)
    dollars = kwargs.pop('dollar', None)
    if (euros is None is dollars) or (euros is not None is not dollars):
        raise ValueError('One keyword argument is required: dollar=x or euro=x')
    elif euros:
        #do something
    else:
        #do something