Как утверждать, что передан ноль или только один из N заданных аргументов - PullRequest
4 голосов
/ 30 октября 2009

У меня есть определение, подобное этому

def bar(self, foo=None, bar=None, baz=None):
    pass

Я хочу убедиться, что максимум из foo, bar, baz пропущен. Я могу сделать

if foo and bar:
    raise Ex()

if foo and baz:
    raise Ex()
....

Но должно быть что-то попроще.

Ответы [ 6 ]

9 голосов
/ 30 октября 2009

Как насчет:

 initialisers = [foo, bar, baz]
 if initialisers.count(None) < len(initialisers) - 1:
     raise Ex()

Он просто подсчитывает, сколько None присутствует. Если они все None или только один не подходит, иначе возникает исключение.

6 голосов
/ 30 октября 2009

x!=None возвращает True (числовое значение 1!) Для не-Nones, False (числовое значение 0) для Nones. Таким образом,

sum(x!=None for x in (foo, bar, baz))

- это самый простой способ подсчитать, сколько из этих идентификаторов связано с ненулевыми значениями (и вы можете проверить, что счетчик равен 1, как и другие ответы для их способов получения счетчика). Это очень общий подход, так как вместо x!=None вы можете использовать любой предикат, представляющий интерес строго bool; например, если у вас есть набор целых чисел и вы хотите узнать, сколько из них имеют 3 в качестве первой цифры своего десятичного представления,

sum(str(abs(x)).startswith('3') for x in (a, b, c, d, e))

тоже отлично работает.

Не стесняйтесь насчет "суммирования bool": bools Python четко определены как подкласс int с ровно двумя экземплярами, которые имеют своеобразный str / repr, но в остальном ведут себя точно так же, как простые целые числа 0 и 1. Есть хорошие прагматические Причины такого замысла и способность выполнять арифметику для bools - одна из них, так что не стесняйтесь использовать эту способность! -)

4 голосов
/ 30 октября 2009

Попробуйте

 count = sum(map(lambda x: 0 if x is None else 1, (foo, bar, baz)))
 if count > 1:
     raise Ex()

Это превращает None в 0 и все в 1, а затем суммирует все.

2 голосов
/ 04 августа 2010

У меня есть еще более короткий ответ, с моей любимой функцией python - декораторами:

def single_keyword(func):
    def single_keyword_dec(*args, **kw):
        if len(kw) > 1:
            raise Exception("More than one initializer passed: {0}".format(kw.keys()))
        return func(*args, **kw)
    return single_keyword_dec

@single_keyword
def some_func(self, foo=None, bar=None, baz=None):
    print foo, bar, baz

some_func(object, foo=0)
some_func(object, foo=0, bar=0)
#result
0 None None
Traceback (most recent call last):
  File "dec2.py", line 13, in <module>
    some_func(object, foo=0, bar=0)
  File "dec2.py", line 4, in single_keyword_dec
    raise Exception("More than one initializer passed: {0}".format(kw.keys()))
Exception: More than one initializer passed: ['foo', 'bar']

Если вам нужно различать «foo», «bar», «baz» и некоторые другие ключевые слова, вы можете создать похожий декоратор, который будет принимать список ключевых слов для ограничения, и использовать его так:

Таким образом, это 100% повторное использование кода, без необходимости вводить одно и то же снова и снова, и вы получаете правильные ключевые слова в своей функции, а не какой-то непонятный дикт.

1 голос
/ 30 октября 2009
if len(filter(lambda x: x != None, locals().values())) > 1:
    raise Exception()

Отредактировано с учетом точки Алекса.

0 голосов
/ 30 октября 2009

Вот так.

def func( self, **kw ):
    assert len(kw) == 1, "Too Many Arguments"
    assert kw.keys[0] in ( 'foo', 'bar', 'baz' ), "Argument not foo, bar or baz"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...