Синтаксис Python для «если a или b или c, но не все из них»

У меня есть скрипт python, который может принимать либо нулевые, либо три аргумента командной строки. (Либо он работает по умолчанию, либо задает все три значения.)

Какой идеальный синтаксис для чего-то вроде:

if a and (not b or not c) or b and (not a or not c) or c and (not b or not a): 

?

15 Solutions collect form web for “Синтаксис Python для «если a или b или c, но не все из них»”

Если вы имеете в виду минимальную форму, переходите к следующему:

 if (not a or not b or not c) and (a or b or c): 

Это переводит название вашего вопроса.

ОБНОВЛЕНИЕ: как правильно сказано волатильность и Supr, вы можете применить закон Де Моргана и получить эквивалент:

 if (a or b or c) and not (a and b and c): 

Мой совет – использовать ту форму, которая важнее для вас и для других программистов. Первое означает «есть что-то ложное, но и что-то истинное» , второе «Есть что-то истинное, но не все» . Если бы я оптимизировал или делал это на аппаратных средствах, я бы выбрал второе, здесь просто выберите наиболее читаемый (также принимая во внимание условия, которые вы будете тестировать, и их имена). Я выбрал первый.

Как насчет:

 conditions = [a, b, c] if any(conditions) and not all(conditions): ... 

Другой вариант:

 if 1 <= sum(map(bool, conditions)) <= 2: ... 

У этого вопроса уже было много ответов с высоким приоритетом и принятый ответ, но все они до сих пор были отвлечены различными способами, чтобы выразить логическую проблему и пропустили решающий момент:

У меня есть скрипт python, который может принимать либо нулевые, либо три аргумента командной строки. (Либо он работает по умолчанию, либо требует всех трех значений)

Во-первых , эта логика не должна нести ответственность за ваш код , скорее, он должен обрабатываться модулем argparse . Не утруждайте себя написанием сложного оператора if, вместо этого предпочитаете настраивать парсер аргументов примерно так:

 #!/usr/bin/env python import argparse as ap parser = ap.ArgumentParser() parser.add_argument('--foo', nargs=3, default=['x', 'y', 'z']) args = parser.parse_args() print(args.foo) 

И да, это должен быть вариант не позиционный аргумент, потому что он, в конце концов, необязателен .


отредактировано: Чтобы рассмотреть проблему LarsH в комментариях, ниже приведен пример того, как вы могли бы написать его, если бы вы были уверены, что хотите интерфейс с 3 или 0 позиционными аргументами. Я придерживаюсь мнения, что предыдущий интерфейс – лучший стиль, потому что необязательные аргументы должны быть опциями , но вот альтернативный подход для полноты. Обратите внимание на переопределяющее usage kwarg при создании вашего синтаксического анализатора, потому что argparse автоматически генерирует вводящее в заблуждение сообщение об использовании!

 #!/usr/bin/env python import argparse as ap parser = ap.ArgumentParser(usage='%(prog)s [-h] [abc]\n') parser.add_argument('abc', nargs='*', help='specify 3 or 0 items', default=['x', 'y', 'z']) args = parser.parse_args() if len(args.abc) != 3: parser.error('expected 3 arguments') print(args.abc) 

Вот несколько примеров использования:

 # default case wim@wim-zenbook:/tmp$ ./three_or_none.py ['x', 'y', 'z'] # explicit case wim@wim-zenbook:/tmp$ ./three_or_none.py 1 2 3 ['1', '2', '3'] # example failure mode wim@wim-zenbook:/tmp$ ./three_or_none.py 1 2 usage: three_or_none.py [-h] [abc] three_or_none.py: error: expected 3 arguments 

Я бы пошел:

 conds = iter([a, b, c]) if any(conds) and not any(conds): # okay... 

Я думаю, что это должно быть достаточно короткое замыкание

объяснение

Сделав conds итератором, первое использование any будет короткое замыкание и оставить итератор, указывая на следующий элемент, если какой-либо элемент является истинным; в противном случае он будет использовать весь список и будет False . Следующий any принимает оставшиеся элементы в итерабельном и гарантирует, что нет других истинных значений … Если есть, весь оператор не может быть правдой, поэтому нет ни одного уникального элемента (так коротко схемы снова). Последний any либо вернет False либо исчерпает итерабельность, и будет True .

Примечание: вышеупомянутые проверки, если установлено только одно условие


Если вы хотите проверить, установлен ли один или несколько элементов, но не каждый элемент, вы можете использовать:

 not all(conds) and any(conds) 

Английское предложение:

«Если a или b или c, но не все из них»

Переводит на эту логику:

 (a or b or c) and not (a and b and c) 

Слово «но» обычно подразумевает соединение, другими словами «и». Кроме того, «все они» переводятся в конъюнкцию условий: это условие и это условие и другое условие. «Не» инвертирует все соединение.

Я не согласен с тем, что принятый ответ. Автор пренебрег применением самой простой интерпретации к спецификации и пренебрег применением закона Де Моргана для упрощения выражения для меньшего числа операторов:

  not a or not b or not c -> not (a and b and c) 

утверждая, что ответ является «минимальной формой».

Это возвращает True если одно и только одно из трех условий – True . Возможно, что вы хотели в вашем примере кода.

 if sum(1 for x in (a,b,c) if x) == 1: 

Как насчет: (уникальное условие)

 if (bool(a) + bool(b) + bool(c) == 1): 

Обратите внимание: если вы разрешите два условия, вы можете сделать это

 if (bool(a) + bool(b) + bool(c) in [1,2]): 

Чтобы быть ясным, вы хотите принять решение, основанное на том, сколько параметров логическое TRUE (в случае строковых аргументов – не пусто)?

 argsne = (1 if a else 0) + (1 if b else 0) + (1 if c else 0) 

Тогда вы приняли решение:

 if ( 0 < argsne < 3 ): doSth() 

Теперь логика более ясна.

И почему бы просто не посчитать их?

 import sys a = sys.argv if len(a) = 1 : # No arguments were given, the program name count as one elif len(a) = 4 : # Three arguments were given else : # another amount of arguments was given 

Если вы не против быть загадочным, вы можете просто перевернуть с помощью 0 < (a + b + c) < 3 который вернет true если у вас есть между одним и двумя истинными операторами и false, если все являются ложными или ни один не является ложным.

Это также упрощает, если вы используете функции для оценки bools, поскольку вы только оцениваете переменные один раз, а это означает, что вы можете писать функции inline и не нужно временно хранить переменные. (Пример: 0 < ( a(x) + b(x) + c(x) ) < 3 .)

Вопрос гласит, что вам нужны либо все три аргумента (a, и b и c), либо ни один из них (не (a или b или c))

Это дает:

(a и b и c) или нет (a или b или c)

Насколько я понимаю, у вас есть функция, которая получает 3 аргумента, но если она не будет работать по умолчанию. Поскольку вы не объяснили, что должно произойти, когда будут предоставлены 1 или 2 аргумента, я предполагаю, что он должен просто выполнять поведение по умолчанию. В этом случае, я думаю, вы найдете следующий ответ очень полезным:

 def method(a=None, b=None, c=None): if all([a, b, c]): # received 3 arguments else: # default behavior 

Однако, если вы хотите обрабатывать 1 или 2 аргумента по-разному:

 def method(a=None, b=None, c=None): args = [a, b, c] if all(args): # received 3 arguments elif not any(args): # default behavior else: # some args (raise exception?) 

note: Предполагается, что значения « False » не будут переданы в этот метод.

Если вы работаете с итератором условий, это может быть медленным для доступа. Но вам не нужно обращаться к каждому элементу более одного раза, и вам не всегда нужно его читать. Вот решение, которое будет работать с бесконечными генераторами:

 #!/usr/bin/env python3 from random import randint from itertools import tee def generate_random(): while True: yield bool(randint(0,1)) def any_but_not_all2(s): # elegant t1, t2 = tee(s) return False in t1 and True in t2 # could also use "not all(...) and any(...)" def any_but_not_all(s): # simple hadFalse = False hadTrue = False for i in s: if i: hadTrue = True else: hadFalse = True if hadTrue and hadFalse: return True return False r1, r2 = tee(generate_random()) assert any_but_not_all(r1) assert any_but_not_all2(r2) assert not any_but_not_all([True, True]) assert not any_but_not_all2([True, True]) assert not any_but_not_all([]) assert not any_but_not_all2([]) assert any_but_not_all([True, False]) assert any_but_not_all2([True, False]) 

Когда каждый данный bool имеет значение True , или когда каждый данный bool является False
все они равны друг другу!

Итак, нам просто нужно найти два элемента, которые оцениваются в разных
знать, что существует хотя бы одно True и по крайней мере одно False .

Мое короткое решение:

 not bool(a)==bool(b)==bool(c) 

Я верю в это короткое замыкание, потому что AFAIK a==b==c равно a==b and b==c .

Мое обобщенное решение:

 def _any_but_not_all(first, iterable): #doing dirty work bool_first=bool(first) for x in iterable: if bool(x) is not bool_first: return True return False def any_but_not_all(arg, *args): #takes any amount of args convertable to bool return _any_but_not_all(arg, args) def v_any_but_not_all(iterable): #takes iterable or iterator iterator=iter(iterable) return _any_but_not_all(next(iterator), iterator) 

Я также написал код с несколькими повторами, но я удалил его отсюда, потому что я думаю, что это бессмысленно. Однако он все еще доступен здесь .

Это в основном «некоторые (но не все)» функциональные возможности (в отличие от функций any() и all() встроенных функций).

Это означает, что между результатами должны быть False s и True . Поэтому вы можете сделать следующее:

 some = lambda ii: frozenset(bool(i) for i in ii).issuperset((True, False)) # one way to test this is... test = lambda iterable: (any(iterable) and (not all(iterable))) # see also http://stackoverflow.com/a/16522290/541412 # Some test cases... assert(some(()) == False) # all() is true, and any() is false assert(some((False,)) == False) # any() is false assert(some((True,)) == False) # any() and all() are true assert(some((False,False)) == False) assert(some((True,True)) == False) assert(some((True,False)) == True) assert(some((False,True)) == True) 

Одно из преимуществ этого кода состоит в том, что вам нужно только повторить его через результирующие (булевы) элементы.

Один из недостатков заключается в том, что все эти выражения истинности всегда оцениваются и не выполняют короткое замыкание подобно операторам and / or операторам.

Interesting Posts

BeautifulSoup получает href

Как мне свернуть элементы в Python?

Почему это решение терпит неудачу? Вложенные и соответствующие скобки

Пользовательская пользовательская модель в django не позволяет устанавливать пароль в admin

IMDB-терапия получает все данные о фильме

Как установить и использовать пакеты Python на Kodingen?

как исправить положение осей с помощью метода set_position в python matplotlib?

Панель инструментов отладки django сломала администратора?

Python: легко получить доступ к глубоко вложенному dict (get и set)

numpy.genfromtxt создает массив того, что выглядит как кортежи, а не 2D-массив – почему?

Python: создать dict из списка и auto-gen / привить ключи (список – это фактические значения клавиш)?

Kodos & "не может ссылаться на открытую группу"

Отображение всплывающих окон в Python (PyQt4)

Можно ли минимизировать консоль в python со стандартным librairy (без дополнительного модуля)?

Как получить несколько параметров с тем же именем из URL-адреса в Pylons?

Python - лучший язык программирования в мире.