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

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

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

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

?

Ответ 1

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

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

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

UPDATE: как правильно сказано волатильностью и Supr, вы можете применить закон Де Моргана и получить эквивалент:

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

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

Ответ 2

Как насчет:

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

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

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

Ответ 3

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

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

Эта логика не должна нести ответственность за ваш код в первую очередь, скорее, он должен обрабатываться модулем 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 позиционными аргументами. Я придерживаюсь мнения, что предыдущий интерфейс - это лучший стиль, потому что необязательные аргументы должны быть опциями, но здесь альтернативный подход для полноты. Обратите внимание на переопределение kwarg usage при создании парсера, потому что argparse будет автоматически генерировать вводящее в заблуждение сообщение об использовании в противном случае!

#!/usr/bin/env python
import argparse as ap
parser = ap.ArgumentParser(usage='%(prog)s [-h] [a b c]\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
[email protected]:/tmp$ ./three_or_none.py 
['x', 'y', 'z']

# explicit case
[email protected]:/tmp$ ./three_or_none.py 1 2 3
['1', '2', '3']

# example failure mode
[email protected]:/tmp$ ./three_or_none.py 1 2 
usage: three_or_none.py [-h] [a b c]
three_or_none.py: error: expected 3 arguments

Ответ 4

Я бы пошел за:

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)

Ответ 5

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

", если 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)

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

Ответ 6

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

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

Ответ 7

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

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

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

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

Ответ 8

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

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

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

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

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

Ответ 9

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

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

Ответ 10

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

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

Ответ 11

Как я понимаю, у вас есть функция, которая получает 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" не будут переданы в этот метод.

Ответ 12

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

Это дает:

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

Ответ 13

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

#!/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])

Ответ 14

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

Итак, нам просто нужно найти два элемента, которые оцениваются в разных bool s
знать, что существует хотя бы один 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)

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

Ответ 15

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

Это означает, что в результатах должны быть False и 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)

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

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