5

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

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

谢谢!

4

3 回答 3

10

您可以在字典键视图上使用 set 操作:

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。

如果您不想允许除and之外的任何其他关键字参数,那么您也可以使用适当的子集测试。仅当左侧集合严格小于右侧集合时,使用两个集合才是 True;它只有比另一组更少的键,并且没有额外的键:dollareuro<

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'密钥不再可接受。

于 2013-10-01T18:11:57.663 回答
5

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

请注意,如果是有效值,则必须使用不同的哨兵None

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)并且永远不会注意到您拼错了“dollar”。

另请注意, raise 的正确异常是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'))
于 2013-10-01T18:12:38.480 回答
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
于 2013-10-01T18:18:45.833 回答