Як ви отримуєте логічний xor двох змінних у Python?


648

Як ви отримуєте логічний 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'

3
Як ви визначаєте "xor" для пари рядків? Що ви вважаєте, "abc" ^ "" повинно повернутись, що це не так?
Мехрдад Афшарі

18
Він повинен повертати True, а не створювати виняток, оскільки лише одна з рядків є True, як визначено типовим типом bool Python.
Зах Гірш

38
Я вражений, що Python не має оператора інфікування під назвою "xor", що було б найбільш інтуїтивно зрозумілим, Pythonic-реалізацією. Використання "^" узгоджується з іншими мовами, але не настільки читабельно, як більшість Python.
Марк Е. Хааз

13
@MehrdadAfshari Очевидною відповіддю на ваше запитання є те, що a xor aвизначено як (a and not b) or (not a and b), і так a xor b, коли aі bє рядки символів, або будь-які інші типи, повинні давати будь-які (a and not b) or (not a and b)врожаї.
Каз

1
Проблема полягає в тому, що документація погана. ^ є "побітним винятком або", що дослівно трактується означає побіжно, а не bool по bool. тому x'FFFF00 '^ x'FFFF00' має бути x'000000 '. Або це означає, що це трапляється лише на основі чару? віддати як числа? Нам потрібно перебрати символи коротших рядків, щоб вони відповідали довжині довшого рядка. Все це має бути вбудовано.
mckenzm

Відповіді:


1187

Якщо ви вже нормалізуєте вхідні дані в булеві, то! = Це xor.

bool(a) != bool(b)

148
Хоча це розумно і коротко, я не переконаний, що це чисто. Коли хтось читає цю конструкцію в коді, чи їм відразу стає очевидно, що це операція xor? Я почував себе зобов'язаним додати коментар - знак для мене, що я пишу незрозумілий код і намагаюся вибачитися з коментарем.

47
Можливо, "чи зрозуміло, що це ХОР?" неправильне питання. Ми просто намагалися зрозуміти, чи відповідь на два питання однаковий, і думали, що ми використаємо XOR для цього. Наприклад, якщо ми хочемо переконатися, що ми не порівнюємо яблука з апельсинами, це "якщо xor (isApple (x), isApple (y))" дійсно зрозуміліше, ніж "if isApple (x)! = IsApple (y)"? Не мені!
AmigoNico

106
Проблема з використанням "! =" Як xor. Ви, напевно, очікували, що bool (a)! = Bool (b)! = Bool (c) буде таким же, як bool (a) ^ bool (b) ^ bool (c). Тож ролі кидають, але я б рекомендував ^. Щоб дізнатися, що відбувається в першому прикладі, знайдіть "прив’язання оператора".
elmo

19
@elmo: +1 для вказівки на різницю та +1 для навчання мене, що таке ланцюжок операторів! Я в таборі, який говорить, що! = Не такий читабельний, як ^.
Марк Е. Хааз

13
це має бути bool(a) is not bool(b)замість цього?
РНК

485

Ви завжди можете використовувати визначення xor для обчислення його з інших логічних операцій:

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

Але це трохи занадто багатослівно для мене, і це не особливо ясно на перший погляд. Ще один спосіб зробити це:

bool(a) ^ bool(b)

Оператор xor на двох булевих елементах є логічним xor (на відміну від ints, де це порозрядне). Що має сенс, оскількиbool є лише підкласомint , але реалізується лише для значень 0і 1. І логічний xor еквівалентний порозрядному xor, коли домен обмежений до 0та1 .

Отже, logical_xorфункція буде реалізовуватися так:

def logical_xor(str1, str2):
    return bool(str1) ^ bool(str2)

Кредит Ніку Коглану в списку розсилки Python-3000 .


7
чудовий пост, але з усіх способів називати свої параметри, чому "str1" і "str2"?
SingleNegationElimination

1
@Token чому ні. Ви маєте на увазі, що вони не дуже пітонічні?
orokusaki

1
@Zach Hirsch Чи можете ви використовувати (не a і b) замість (b і не a) для читабельності, чи це визначення буде невідповідним xor.
orokusaki

10
Ви повинні поставити nots спочатку (not b and a) or (not a and b)так, щоб він повернув рядок, якщо такий був, що здається пітонічним способом функціонування функції.
rjmunro

2
@TokenMacGuy: Що ти запропонував, щоб він назвав їх замість них?
користувач541686

180

Побітовий ексклюзив - або вже вбудований в Python в operatorмодулі (який ідентичний ^оператору):

from operator import xor
xor(bool(a), bool(b))  # Note: converting to bools is essential

3
Це те, що мені було потрібно. При зворотному інженерному зловмисному програмі багато разів рядки обробляють до операції XOR Використовуючи цей chr (xor (ord ("n"), 0x1A)) = 't'
ril3y

75
Будьте уважні, це також побіжно: xor(1, 2)повертається 3. З docstring: xor(a, b) -- Same as a ^ b. Пам’ятайте, що все, що імпортується, operator- це лише функціональна форма існуючого вбудованого оператора інфіксації.
askewchan

5
@askewchan: boolТип перевантажує, __xor__щоб повернути булі. Він буде працювати чудово, але його надмірність коли bool(a) ^ bool(b)робить точно те саме.
Martijn Pieters

@MartijnPieters ^Оператор телефонує __xor__внутрішньо.
Quantum7

5
@ Quantum7: так, я не впевнений, чому ти мені це кажеш. Я щойно сказав, що boolтип реалізує __xor__метод саме тому, що ^викликає його . Справа в тому, що це bool(a) ^ bool(b)працює добре, тут не потрібно використовувати цю operator.xor()функцію.
Martijn Pieters

43

Як пояснив Зак , ви можете використовувати:

xor = bool(a) ^ bool(b)

Особисто я віддаю перевагу дещо іншому діалекту:

xor = bool(a) + bool(b) == 1

Цей діалект натхненний мовою логічного діаграмування, яку я вивчив у школі, де "АБО" позначався коробкою, що містить ≥1(більше або дорівнює 1), а "XOR" позначається коробкою, що містить =1.

Це має перевагу в правильній реалізації ексклюзивних або на декількох операндах.

  • "1 = a ^ b ^ c ..." означає кількість істинних операндів непарне. Цей оператор - «паритет».
  • "1 = a + b + c ..." означає точно один операнд. Це "виключно або", що означає "одне до виключення інших".

12
Так, True + True + False + True == 3 і 3! = 1, але True XOR True XOR False XOR True == True. Чи можете ви детальніше зупинитися на "правильній реалізації XOR на кількох операндах"?
tzot

3
@tzot Ваш приклад не вдається, оскільки згідно з рішенням ddaa, ви додаєте додавання одночасно лише до двох змінних. Тож правильним способом все це було б бути (((((True + True)==1)+False)==1)+True)==1. Відповідь, дана тут, повністю узагальнює декілька операндів.
ely

6
Крім того, існує різниця між тристороннім XOR проти набором операцій, згрупованим набором двох XOR. Отже, 3-WAY-XOR (A, B, C) - це не те саме, що XOR (XOR (A, B), C). Приклад ddaa - перший, а ваш припускає другий.
ely

3
@ Mr.F Ваше пояснення насправді не виправдає цю відповідь. В Python, якщо ви просто робите True + True + False + True, ви дійсно отримаєте 3, і True + True + False + True == 3повертає в Trueтой час True + True + False + True == 1віддає False. Іншими словами, відповідь тут не є узагальненою правильно; щоб це зробити, потрібно зробити додаткову роботу. Тим часом проста True ^ True ^ False ^ Trueробота працює, як і очікувалося.
jpmc26

3
@ jpmc26 Я не розумію твій коментар. Підхід додавання призначений для узагальнення операції, в якій ви хочете перевірити, що саме один операнд - Trueце багаторядний XOR. Це інша операція , ніж, наприклад, A XOR B XOR ... XOR Z. Іншими словами, якщо ви плануєте використовувати версію на основі додавання, то після подання операндів у True + True + False + Trueвас слід очікувати, що результат буде Falseбільшим, ніж один із них True, який працює, якщо умова перевіряє == 1.
ely

26
  • Python логічні or: A or B: повертається , Aякщо bool(A)є True, в іншому випадку повертаєтьсяB
  • Python логічні and: A and B: повертається , Aякщо bool(A)є False, в іншому випадку повертаєтьсяB

Щоб дотримуватися більшості такого способу мислення, моїм логічним визначенням xor було б:

def logical_xor(a, b):
    if bool(a) == bool(b):
        return False
    else:
        return a or b

Таким чином , він може повернутися a, bабо False:

>>> logical_xor('this', 'that')
False
>>> logical_xor('', '')
False
>>> logical_xor('this', '')
'this'
>>> logical_xor('', 'that')
'that'

5
Мені це здається поганим або, принаймні, дивним. Жоден з інших вбудованих логічних операторів не повертає одне з трьох можливих значень.
Zach Hirsch

2
@Zach Hirsch: Ось чому я сказав "дотримуватися більшості такого способу мислення" - оскільки немає хорошого результату, коли обидва вірні чи помилкові
nosklo

Логічна операція повинна повертати логічне значення, тому друге "return a або b" виглядає дивним, тому друге повернення повинно повернути True.
Денис Барменков

9
@Denis барменку: Ну, зверніть увагу , що пітон логічні оператори andі orне повертатиме логічне значення. 'foo' and 'bar'повертається 'bar'...
nosklo

6
На перший погляд, дві попередні відповіді здаються найкращими, але, по-друге, ця насправді є єдиною справді правильною, тобто є єдиною, яка дає приклад xorреалізації, який відповідає вбудованому andта or. Однак, звичайно, в практичних ситуаціях, bool(a) ^ bool(b)або навіть a ^ b(якщо aі , bяк відомо bool) є більш короткими, звичайно.
Ерік Каплун

23

Я перевірив кілька підходів і not a != (not b) виявився найшвидшим.

Ось кілька тестів

%timeit not a != (not b)
10000000 loops, best of 3: 78.5 ns per loop

%timeit bool(a) != bool(b)
1000000 loops, best of 3: 343 ns per loop

%timeit not a ^ (not b)
10000000 loops, best of 3: 131 ns per loop

Редагувати: у прикладах 1 та 3 вище відсутні дужки, тому результат неправильний. Нові результати + truth()функція, як запропонував ShadowRanger.

%timeit  (not a) ^  (not b)   # 47 ns
%timeit  (not a) != (not b)   # 44.7 ns
%timeit truth(a) != truth(b)  # 116 ns
%timeit  bool(a) != bool(b)   # 190 ns

6
Це 100 нс мого життя, я не повернусь ;-)
Арель,

4
Для тимчасового проміжку часу ви можете зробити це from operator import truthу верхній частині модуля та протестувати truth(a) != truth(b). boolбудучи конструктором, існує багато неминучих накладних витрат на рівні С (він повинен приймати аргументи як еквівалент *args, **kwargsі аналізувати tupleта dictвитягувати їх), де truth(будучи функцією) може використовувати оптимізований шлях, який не вимагає ані tupleабо a dict, і працює приблизно в половині часу на boolоснові рішень (але все ж довше, ніж notзаснованих на рішеннях).
ShadowRanger

9

Нагородження нитки:

Ідея анодера ... Просто ви спробуйте (може бути) пітонічний вираз «не», щоб отримати поведінку логічного «xor»

Таблиця правди:

>>> True is not True
False
>>> True is not False
True
>>> False is not True
True
>>> False is not False
False
>>>

А для вашого прикладу рядок:

>>> "abc" is not  ""
True
>>> 'abc' is not 'abc' 
False
>>> 'abc' is not '' 
True
>>> '' is not 'abc' 
True
>>> '' is not '' 
False
>>> 

Однак; як вони вказали вище, це залежить від фактичної поведінки, яку ви хочете витягнути про будь-які пару струн, тому що струни не є boleans ... і навіть більше: якщо ви «зануриться в Python», ви знайдете «Своєрідний характер» і "і" або "» http://www.diveintopython.net/power_of_introspection/and_or.html

Вибачте за мою написану англійську, це не моя рідна мова.

З повагою


Я також використовую, щоб прочитати це як "суворо інше". Це тому, що деякі мови використовують, щоб реалізувати операцію по бітах бінарного представлення і приймати bool отриманої побітової операції. Я здогадуюсь, що ваша відповідь більш "захищена типом", оскільки вона виходить за межі булевого простору.
Юсер

Я маю на увазі той факт, що ваша відповідь охоплює випадок порівняння None, False, '' як різного - це відмінні речі. Наприклад: bool (False)! = Bool (''), тим не менше, False не є '' "більше погоджується з цією семантикою" суворо інакше "
yucer

8

У Python є ексклюзивний оператор АБО АБО ^:

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

Ви можете використовувати його, перетворивши вхідні дані в булі, перш ніж застосувати xor ( ^):

bool(a) ^ bool(b)

(Відредаговано - дякую Арел)


У вашій відповіді повинно бути зрозуміло, що ^це бітовий xor (не логічний xor, як задане питання). bool(2) ^ bool(3)дає іншу відповідь, ніж bool(2 ^ 3).
Арель

1
@Arel Але це не так. a ^ bє поліморфом. Якщо aі bє boolвипадки, результат буде boolтакож. Таку поведінку навряд чи можна назвати "побитним" хором.
Альфа

@ Важливим моментом є те, що значення повинні бути передані булевим спочатку. Документація Python визначає ^як бітові, хоча цікавий момент збереження типів boolта intтипів. Примітка: True ^ 2це 3, демонструючи, як це насправді побито.
Арель

@Arel Так, bool ^ intсправа начебто все intспочатку. Тим НЕ менше, Python має вбудований в ^операторі для багатьох біт intі для одного біта представлені в bool, так як це побітовое , але побітового виключає для одного біта просто є логічним виключає для булеві.
Альфа

Я завжди ненавиджу використання цього оператора, хоча я розумію, що це xor, виходячи з інженерного походження, для мене це інстинктивно відчуває як математичну силу, тобто, 2^3 = pow(2,3)я завжди явно коментую, щоб запобігти плутанині.
Ніколас Гамільтон

8

Оскільки я не бачу простого варіанту xor, що використовує змінні аргументи та лише операції зі значеннями Truth True або False, я просто кину його сюди для будь-кого. Як зазначають інші, досить (не сказати дуже) прямо.

def xor(*vars):
    sum = False
    for v in vars:
        sum = sum ^ bool(v)
    return sum

І використання також просто:

if xor(False, False, True, False):
    print "Hello World!"

Оскільки це узагальнений n-арний логічний XOR, це значення істини буде True, коли число True операндів непарне (і не тільки тоді, коли саме один True, це лише один випадок, коли n-arry XOR є True).

Таким чином, якщо ви шукаєте n-ary присудок, який є істинним лише тоді, коли саме один з його операндів є, ви можете використовувати:

def isOne(*vars):
    sum = False
    for v in vars:
        if sum and v:
            return False
        else:
            sum = sum or v
    return sum

Для поліпшення цієї відповіді: (bool(False) is False) == True. Ви можете просто використовувати Falseна цих лініях.
pathunstrom

7

Виключний або визначається наступним чином

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

2
що поверне True для xor ('цей', '') і щоб слідувати шляху python, він повинен повернути "this".
nosklo

@nosklo: Візьміть це за BDFL, будь ласка, не я. Оскільки Python повертає True, то це повинен бути шлях Python.
С.Лотт

2
Я маю на увазі для узгодженості з іншими логічними операторами python - Python не повертає True, коли я це роблю ("це" або ""), він повертає "це". Але у вашій функції xor ('це', '') повертає True. Він повинен повернути "це" так, як це робиться "або" python.
nosklo

10
Python andі orробити коротке замикання. Будь-яка xorреалізація не може мати короткого замикання, тому вже є розбіжність; тому немає жодної причини, яка xorповинна діяти як and+ ordo.
tzot

7

Іноді мені здається, що я працюю з 1 і 0 замість булевих значень True та False. У цьому випадку xor можна визначити як

z = (x + y) % 2

яка має таку таблицю істинності:

     x
   |0|1|
  -+-+-+
  0|0|1|
y -+-+-+
  1|1|0|
  -+-+-+

7

Я знаю, що це пізно, але я подумав, і це, можливо, варто лише для документації. Можливо, це спрацювало б: np.abs(x-y)ідея така

  1. якщо x = True = 1 і y = False = 0, то результат буде | 1-0 | = 1 = True
  2. якщо x = False = 0 і y = False = 0, то результат буде | 0-0 | = 0 = False
  3. якщо x = True = 1 і y = True = 1, результат буде | 1-1 | = 0 = Неправильно
  4. якщо x = False = 0 і y = True = 1, результат буде | 0-1 | = 1 = True

7

Простий, зрозумілий:

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

Якщо ексклюзивний вибір - це те, що ви хочете, його можна розширити на кілька аргументів:

sum( bool(x) for x in y ) % 2 == 1

1
sum(map(bool, y)) % 2 == 1
Варварюк

6

Як щодо цього?

(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

Деякі із запропонованих тут реалізацій спричинять неодноразову оцінку операндів у деяких випадках, що може призвести до непередбачуваних побічних ефектів, і тому їх слід уникати.

Однак, xorреалізація, яка повертає Trueабо Falseє досить простою; той, який повертає один із операндів, якщо це можливо, набагато складніше, оскільки не існує єдиної думки щодо того, який операнд повинен бути обраним, особливо, коли є більше двох операндів. Наприклад, повинен xor(None, -1, [], True)повернутися None, []або 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()

5

Багатьом людям, включаючи мене, потрібна xorфункція, яка поводиться як n-вхідна схема xor, де 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

5

Щоб отримати логічний xor двох або більше змінних у Python:

  1. Перетворення входів на булеві
  2. Використовуйте оператор побітового xor ( ^або operator.xor)

Наприклад,

bool(a) ^ bool(b)

Коли ви перетворюєте входи в булеві, побітовий xor стає логічним xor.

Зауважте, що прийнята відповідь є невірною: != не тотожна xor в Python через тонкість ланцюга оператора .

Наприклад, xor з трьох наведених нижче значень є неправильним при використанні !=:

True ^  False ^  False  # True, as expected of XOR
True != False != False  # False! Equivalent to `(True != False) and (False != False)`

(PS Я спробував редагувати прийняту відповідь, щоб включити це попередження, але мою зміну було відхилено.)


4

Це легко, коли ви знаєте, що робить 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

Це отримує логічний ексклюзивний XOR для двох (або більше) змінних

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

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

Перша проблема цієї установки полягає в тому, що вона, швидше за все, двічі проходить весь список і, як мінімум, двічі перевірить принаймні один з елементів. Таким чином, це може збільшити розуміння коду, але воно не надає швидкості (що може незначно відрізнятися залежно від випадку використання).

Друга проблема цієї установки полягає в тому, що вона перевіряє ексклюзивність незалежно від кількості змінних. Спочатку це може розглядатися як особливість, але перша проблема стає набагато більш значною, оскільки кількість змінних збільшується (якщо вони коли-небудь є).


4

Xor знаходиться ^в Python. Він повертає:

  • Бітовий xor для ints
  • Логічний xor для bools
  • Ексклюзивна спілка для наборів
  • Результати, визначені користувачем для класів, які реалізуються __xor__.
  • TypeError для невизначених типів, таких як рядки або словники.

Якщо ви все одно маєте намір використовувати їх на рядках, їх кастинг boolробить вашу операцію однозначною (ви також можете мати на увазі set(str1) ^ set(str2)).



3

Ось так я б кодував будь-яку таблицю істинності. Зокрема, для xor ми маємо:

| a | b  | xor   |             |
|---|----|-------|-------------|
| T | T  | F     |             |
| T | F  | T     | a and not b |
| F | T  | T     | not a and b |
| F | F  | F     |             |

Просто подивіться на значення T у стовпці відповідей та з'єднайте всі справжні випадки з логічним або. Отже, ця таблиця істинності може бути складена у випадку 2 або 3. Отже,

xor = lambda a, b: (a and not b) or (not a and b)

-6

Ми можемо легко знайти xor двох змінних, використовуючи:

def xor(a,b):
    return a !=b

Приклад:

xor (Правда, помилково) >>> Істинно


1
або xor("hey", "there")>>> Щоправда, але це не те, що ми хочемо
Mayou36
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.