Как вы получаете логический xor двух переменных в Python? - PullRequest
544 голосов
/ 11 января 2009

Как вы получаете логический xor двух переменных в Python?

Например, у меня есть две переменные, которые я ожидаю, чтобы быть строками. Я хочу проверить, что только один из них содержит значение True (не None или пустую строку):

str1 = raw_input("Enter string one:")
str2 = raw_input("Enter string two:")
if logical_xor(str1, str2):
    print "ok"
else:
    print "bad"

Оператор ^ представляется побитовым и не определен для всех объектов:

>>> 1 ^ 1
0
>>> 2 ^ 1
3
>>> "abc" ^ ""
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for ^: 'str' and 'str'

Ответы [ 21 ]

7 голосов
/ 18 апреля 2017

Просто, легко понять:

sum( (bool(a), bool(b) ) == 1

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

sum( bool(x) for x in y ) == 1
6 голосов
/ 11 января 2009

Как насчет этого?

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

даст a, если b ложно
выдаст b, если a ложно
даст False иначе

Или с троичным выражением Python 2.5+:

(False if a else b) if b else a
6 голосов
/ 08 марта 2014

Иногда я работаю с 1 и 0 вместо логических значений True и False. В этом случае xor может быть определен как

z = (x + y) % 2

, которая имеет следующую таблицу истинности:

     x
   |0|1|
  -+-+-+
  0|0|1|
y -+-+-+
  1|1|0|
  -+-+-+
6 голосов
/ 04 июля 2009

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

Тем не менее, реализация xor, которая возвращает либо True, либо False, довольно проста; тот, который возвращает один из операндов, если это возможно, гораздо хитрее, потому что не существует единого мнения относительно того, какой операнд должен быть выбранным, особенно когда имеется более двух операндов. Например, xor(None, -1, [], True) должен возвращать None, [] или False? Могу поспорить, что каждый ответ кажется некоторым людям наиболее интуитивным.

Для True- или False-результата существует пять возможных вариантов: вернуть первый операнд (если он соответствует конечному результату по значению, иначе булево), вернуть первое совпадение (если хотя бы один существует, иначе логическое значение), возвращает последний операнд (если ... еще ...), возвращает последнее совпадение (если ... еще ...) или всегда возвращает логическое значение. В целом, это 5 ** 2 = 25 вкусов xor.

def xor(*operands, falsechoice = -2, truechoice = -2):
  """A single-evaluation, multi-operand, full-choice xor implementation
  falsechoice, truechoice: 0 = always bool, +/-1 = first/last operand, +/-2 = first/last match"""
  if not operands:
    raise TypeError('at least one operand expected')
  choices = [falsechoice, truechoice]
  matches = {}
  result = False
  first = True
  value = choice = None
  # avoid using index or slice since operands may be an infinite iterator
  for operand in operands:
    # evaluate each operand once only so as to avoid unintended side effects
    value = bool(operand)
    # the actual xor operation
    result ^= value
    # choice for the current operand, which may or may not match end result
    choice = choices[value]
    # if choice is last match;
    # or last operand and the current operand, in case it is last, matches result;
    # or first operand and the current operand is indeed first;
    # or first match and there hasn't been a match so far
    if choice < -1 or (choice == -1 and value == result) or (choice == 1 and first) or (choice > 1 and value not in matches):
      # store the current operand
      matches[value] = operand
    # next operand will no longer be first
    first = False
  # if choice for result is last operand, but they mismatch
  if (choices[result] == -1) and (result != value):
    return result
  else:
    # return the stored matching operand, if existing, else result as bool
    return matches.get(result, result)

testcases = [
  (-1, None, True, {None: None}, [], 'a'),
  (None, -1, {None: None}, 'a', []),
  (None, -1, True, {None: None}, 'a', []),
  (-1, None, {None: None}, [], 'a')]
choices = {-2: 'last match', -1: 'last operand', 0: 'always bool', 1: 'first operand', 2: 'first match'}
for c in testcases:
  print(c)
  for f in sorted(choices.keys()):
    for t in sorted(choices.keys()):
      x = xor(*c, falsechoice = f, truechoice = t)
      print('f: %d (%s)\tt: %d (%s)\tx: %s' % (f, choices[f], t, choices[t], x))
  print()
4 голосов
/ 09 января 2012

XOR реализован в operator.xor.

4 голосов
/ 10 февраля 2011

Легко, когда вы знаете, что делает XOR:

def logical_xor(a, b):
    return (a and not b) or (not a and b)

test_data = [
  [False, False],
  [False, True],
  [True, False],
  [True, True],
]

for a, b in test_data:
    print '%r xor %s = %r' % (a, b, logical_xor(a, b))
4 голосов
/ 08 апреля 2015

Получает логический эксклюзивный XOR для двух (или более) переменных

str1 = raw_input("Enter string one:")
str2 = raw_input("Enter string two:")

any([str1, str2]) and not all([str1, str2])

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

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

3 голосов
/ 04 марта 2019

Xor - это ^ в Python. Возвращает:

  • Побитовый xor для целых чисел
  • Логический xor для bools
  • Эксклюзивный союз для комплектов
  • Определенные пользователем результаты для классов, которые реализуют __xor__.
  • TypeError для неопределенных типов, таких как строки или словари.

Если вы все равно намереваетесь использовать их для строк, приведение их к bool сделает вашу операцию однозначной (вы также можете обозначить set(str1) ^ set(str2)).

3 голосов
/ 14 марта 2019

Многим, в том числе и мне, нужна функция xor, которая ведет себя как схема x-n-входа, где n - переменная. (См. https://en.wikipedia.org/wiki/XOR_gate). Следующая простая функция реализует это.

def xor(*args):
   """
   This function accepts an arbitrary number of input arguments, returning True
   if and only if bool() evaluates to True for an odd number of the input arguments.
   """

   return bool(sum(map(bool,args)) % 2)

Пример ввода / вывода:

In [1]: xor(False, True)
Out[1]: True

In [2]: xor(True, True)
Out[2]: False

In [3]: xor(True, True, True)
Out[3]: True
0 голосов
/ 07 июня 2019

Python имеет оператор исключающего ИЛИ, это ^,

Так что нет никакой необходимости в какой-либо библиотеке или изобретать колесо самостоятельно.

>>> True ^ False
True
>>> True ^ True
False
>>> False ^ True
True
>>> False ^ False
False
...