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

时间:2013-10-01 18:09:14

标签: python

我有一个可以接受关键字参数的函数,我想要其中一个。让我们说美元和欧元,我只想要其中一个。现在,我这样做(例证),但我觉得它很复杂。还有其他更好的方法吗?

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

谢谢!

3 个答案:

答案 0 :(得分:8)

您可以在dictionary keys view上使用设置操作:

if len(kargs.viewkeys() & {'dollar', 'euro'}) != 1:
    raise ValueError('One keyword argument is required: dollar=x or euro=x')

在Python 3中,改为使用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.

如果您不希望在dollareuro之外允许任何其他关键字参数,那么您也可以使用正确的子集测试。如果左侧集合严格小于右侧集合,则将<与两个集合使用仅为True;它只有更少的键而不是其他组,而且没有额外的键:

if {}.viewkeys() < kargs.viewkeys() < {'dollar', 'euro'}:
    raise ValueError('One keyword argument is required: dollar=x or euro=x')

在Python 3上,可拼写为:

if set() < kargs.keys() < {'dollar', 'euro'}:

代替。

演示:

>>> 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

请注意,现在'foo'密钥已不再可接受。

答案 1 :(得分:2)

我们不要使用kargs.keys()[0],因为这取决于键的顺序,这是未指定的。它现在 ,但它很脆弱,因为如果你添加另一个关键字参数或迁移到Python 3它会破坏。

请注意,您必须使用different sentinel if None is a valid value

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

一般情况下,我试图远离使用**kwargs,除非我将关键字参数传递给另一个函数。如果您使用**kwargs,则可以拨打set_value(dolar=5),但不要注意到拼写错误的“美元”。

另请注意,引发的正确例外是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'))

答案 2 :(得分:0)

您可以使用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