Разница между " и "(логическое) и " & " (побитовое) в python. Почему разница в поведении со списками и массивами numpy?

что объясняет разницу в поведении логических и побитовых операций над списками против numpy.массивы?

я путаюсь о соответствующем использовании '&' vs'and ' в python, иллюстрируется в следующих простых примерах.

    mylist1 = [True,  True,  True,  False,  True]
    mylist2 = [False, True, False,  True, False]  

    >>> len(mylist1) == len(mylist2)
    True

    # ---- Example 1 ----
    >>>mylist1 and mylist2 
    [False, True, False, True, False]
    #I am confused: I would have expected [False, True, False, False, False]

    # ---- Example 2 ----
    >>>mylist1 & mylist2 
    *** TypeError: unsupported operand type(s) for &: 'list' and 'list'
    #I am confused: Why not just like example 1? 

    # ---- Example 3 ----
    >>>import numpy as np

    >>> np.array(mylist1) and np.array(mylist2) 
    *** ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
    #I am confused: Why not just like Example 4? 

     # ---- Example 4 ----
    >>> np.array(mylist1) & np.array(mylist2) 
    array([False,  True, False, False, False], dtype=bool)
    #This is the output I was expecting! 

этой ответ, а это ответ оба помогли мне понять, что 'и' является логической операцией, но '&' - это побитовое операция.

Я чтение информация, чтобы лучше понять концепцию побитовые операции, но я изо всех сил пытаюсь использовать эту информацию, чтобы понять Мои выше 4 примера.

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

    len(newlist) == len(mylist1) 
    newlist[i] == (mylist1[i] and mylist2[i]) #for every element of newlist

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

но я остался в замешательстве о том, когда/как/почему я должен использовать 'и' против '&'. Почему списки и массивы numpy ведут себя по-разному с этими операторами?

может ли кто-нибудь помочь мне понять разницу между логическими и побитовыми операциями, чтобы объяснить, почему они обрабатывают списки и numpy.массивы по-другому?

Я просто хочу убедиться,что я продолжаю правильно использовать эти операции. Большое спасибо за помощь!

Numpy version 1.7.1

python 2.7

References all inline with text.

редактирование

1) Спасибо @delnan за указание то, что в моих первоначальных примерах у меня была двусмысленность, маскировавшая мое более глубокое замешательство. Я обновил свои примеры, чтобы прояснить свой вопрос.

7 ответов


and проверяет, являются ли оба выражения являются логически True пока & (при использовании True/False значения) тесты, Если оба True.

в Python пустые встроенные объекты обычно рассматриваются как логически False в то время как непустые встроенные модули логически True. Это облегчает общий случай использования, когда вы хотите сделать что-то, если список пуст, и что-то еще, если список не является. Обратите внимание, что это означает, что список [False] логически True:

>>> if [False]:
...    print 'True'
...
True

Итак, в Примере 1 первый список непустой и, следовательно, логически True, поэтому значение истины and то же самое, что и во втором списке. (В нашем случае второй список непустой и поэтому логически True, но определение этого потребует ненужного шага вычисления.)

например 2, списки не могут быть объединены побитовым способом, поскольку они могут содержать произвольные непохожие элементы. Вещи, которые могут быть объединены побитовые включают: Trues и Falses, целые числа.

объекты NumPy, напротив, поддерживают векторизованные вычисления. То есть они позволяют выполнять одни и те же операции с несколькими частями данных.

Пример 3 терпит неудачу, потому что массивы NumPy (длины > 1) не имеют значения истинности, поскольку это предотвращает векторную логическую путаницу.

Пример 4 - это просто векторизованный бит and операции.

дно Линия

  • если вы не имеете дело с массивами и не выполняете математические манипуляции целых чисел, вы, вероятно, хотите and.

  • если у вас есть векторы значений истинности, которые вы хотите объединить, используйте numpy С &.


булевы операторы короткого замыкания (and, or) не может быть переопределен, потому что нет удовлетворительного способа сделать это без введения новых языковых функций или отказа от короткого замыкания. Как вы можете знать или не знать, они оценивают первый операнд для его значения истинности, и в зависимости от этого значения либо оценивают и возвращают второй аргумент, либо не оценивают второй аргумент и возвращают первый:

something_true and x -> x
something_false and x -> something_false
something_true or x -> something_true
something_false or x -> x

обратите внимание, что (результат оценки) фактический операнд, а не его истинностное значение.

единственный способ настроить их поведение переопределить __nonzero__ (переименован в __bool__ в Python 3), поэтому вы можете повлиять на то, какой операнд возвращается, но не возвращать что-то другое. Списки (и другие коллекции) определяются как "истинные", когда они содержат что-либо вообще, и" ложные", когда они пусты.

массивы NumPy отвергают это понятие: для случаев использования, на которые они нацелены, два разных понятия истины общее: (1) истинен ли какой-либо элемент и (2) истинны ли все элементы. Поскольку эти два полностью (и молча) несовместимы, и ни один из них явно не является более правильным или более распространенным, NumPy отказывается угадывать и требует, чтобы вы явно использовали .any() или .all().

& и |not, кстати) can быть полностью исключен, так как они не короткое замыкание. Они могут возвращать что угодно, когда переопределены, и NumPy хорошо использует это для этого элементарные операции, как и практически любая другая скалярная операция. Списки, с другой стороны, не транслируют операции по своим элементам. Так же, как mylist1 - mylist2 ничего не значит, а mylist1 + mylist2 означает что-то совершенно другое, нет & оператора для списков.


о list

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

в обычном Python,list не является особенным в любом случае (за исключением милого синтаксиса для построения, который в основном является исторической случайностью). Один раз список [3,2,6] сделано это, для всех намерений и целей, просто обычный объект в Python, как количество 3, set {3,7}, или функция lambda x: x+5.

(Да, он поддерживает изменение его элементов, и он поддерживает итерацию и многое другое, но это именно то, что тип: он поддерживает некоторые операции, не поддерживая некоторые другие. int поддерживает повышение до власти, но это не делает его особенным - это просто то, что int. лямбда поддерживает вызов, но это не делает его особенным - это то, для чего лямбда, в конце концов:).

о and

and не является оператором (вы можете назвать его "оператор", но вы можете вызвать "для" оператора слишком.): Операторы в Python (реализуются через) методы, вызываемые для объектов некоторого типа, обычно записываемых как часть этого типа. Нет способа для метода провести оценку некоторых из его операндов, но and может (и должен) это сделать.

следствием этого является то, что and не может быть перегружен, так как for не могут быть перегружены. Он является полностью общим и взаимодействует через определенный протокол. Что ты!--52-- > can do настраивает Вашу часть протокола, но это не означает, что вы можете изменить поведение and полностью. Протокол такой:

представьте, что Python интерпретирует "a и b" (это не происходит буквально так, но это помогает понять). Когда дело доходит до" и", он смотрит на объект, который он только что оценил (а), и спрашивает его: вы правы? (не: ты True?) Если вы являетесь автором класса, вы можете настроить этот ответ. Если a ответ "нет", and (пропускает b полностью он вообще не оценивается, а) говорит:a мой результат (не: False-мой результат).

если a не отвечает and спрашивает: какова ваша длина? (Опять же, вы можете настроить это как автор С). Если a ответы 0, and делает то же, что и выше - считает его ложным (не False), пропускает b и дает a как результат.

если a отвечает что-то другое, чем 0 на второй вопрос ("какова ваша длина"), или он вообще не отвечает, или он отвечает "Да" на первый ("вы верны"),and оценивает b и говорит:b - это мой результат. Обратите внимание, что это делает не задать b любые вопросы.

другой способ сказать все это, что a and b почти то же самое, что b if a else a, кроме a оценивается только один раз.

теперь посидите несколько минут с ручкой и бумагой и убедитесь, что когда {a, b} является подмножеством {True, False}, он работает точно так, как вы ожидали бы от булевых операторов. Но, надеюсь, я убедил вас, что она гораздо более общая и, как вы увидите, гораздо более полезная.

соединяя эти два вместе

теперь я надеюсь, что вы понимаете свой пример 1. and не волнует, является ли mylist1 числом, списком, лямбдой или объектом класса Argmhbl. Он просто заботится об ответе mylist1 на вопросы протокола. И конечно, mylist1 отвечает на вопрос 5 о длине, так и возвращает mylist2. И это все. Это не имеет ничего общего с элементами mylist1 и mylist2 - они нигде не входят в картину.

второй пример: & on list

с другой стороны, & - это оператор, как и любой другой, как + например. Его можно определить для типа, определив специальный метод для этого класса. int определяет его как побитовое "и", А bool определяет его как логическое "и", но это только один вариант: например, множества и некоторые другие объекты, такие как представления ключей dict, определяют его как пересечение множества. list просто не определяет его, вероятно, потому, что Гвидо не думал о каком-либо очевидном способе его определения.

включает в себя

на другой ноге: - D, numpy массивы are специальные, или по крайней мере они пытаются быть. Конечно, тупица.массив-это просто класс, он не может переопределить and в любом случае, поэтому он делает следующее лучшее: когда его спрашивают "Ты правда", numpy.матрица поднимает ValueError, эффективно говоря :" пожалуйста, перефразируйте вопрос, мой взгляд на истину не вписывается в вашу модель". (Обратите внимание, что сообщение ValueError не говорит о and - потому что numpy.array не знает кто задает ему вопрос; он просто говорит о правде.)

на &, это совершенно другая история. и NumPy.array может определить его, как он хочет, и он определяет & последовательно с другими операторами: pointwise. Итак, Вы, наконец, получить то, что вы хотите.

HTH,


Пример 1:

это как и работает оператор.

x и y => если x ложно, то x, else y

так Другими словами, с mylist1 не False, результатом выражения является mylist2. (Только пустые списки оценка для False.)

пример 2:

на & оператор предназначен для побитового и, как вы упомянули. Побитовые операции работают только с числами. Результат a & b - это число, состоящее из 1s в битах, которые равны 1 в обоих a и b. Например:

>>> 3 & 1
1

легче увидеть, что происходит, используя двоичный литерал (те же номера, что и выше):

>>> 0b0011 & 0b0001
0b0001

побитовые операции аналогичны в понятие логические операции (правда), но они работают только на битах.

Итак, учитывая пару заявлений о моей машине

  1. моя машина красная
  2. у моей машины есть колеса

логическим "и" этих двух утверждений является:

(моя машина красная?) и (есть ли у автомобиля колеса?) = > логическое true значения false

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

(числовое значение оператора "мой автомобиль красный") & (числовое значение оператора "мой автомобиль имеет колеса") = > number

если python знает, как преобразовать операторы в числовые значения, то он сделает это и вычислит побитовое и из двух значений. Это может привести вас к мысли, что & is взаимозаменяемый с and, но, как и в приведенном выше примере это разные вещи. Кроме того, для объектов, которые не могут быть преобразованы, вы просто получите TypeError.

Пример 3 и 4:

библиотеки numpy реализует арифметические операции для массивов:

арифметические операции и операции сравнения на ndarrays определяются как операции по элементам и обычно дают объекты ndarray как результаты.

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

Итак, чтобы ответить на ваш and vs & вопрос: Использовать and.

побитовые операции используются для изучения структуры числа (какие биты установлены, какие биты не установлены). Этот вид информации главным образом использован внутри интерфейсы низкоуровневой операционной системы (биты разрешений unix, например). Большинство программ python не должны знать этого.

логические операции (and, or, not), однако, используются все время.


  1. в Python выражение X and Y возвращает Y, учитывая, что bool(X) == True или какой-либо из X или Y оценить на False, например:

    True and 20 
    >>> 20
    
    False and 20
    >>> False
    
    20 and []
    >>> []
    
  2. побитовый оператор просто не определен для списков. Но он определен для целых чисел-работающих над двоичным представлением чисел. Рассмотрим 16 (01000) и 31 (11111):

    16 & 31
    >>> 16
    
  3. NumPy не экстрасенс, он не знает, имеете ли вы в виду что например,[False, False] должно быть равно True в логическом выражении. В этом случае он переопределяет стандартное поведение Python, которое: "любая пустая коллекция с len(collection) == 0 is False".

  4. вероятно, ожидаемое поведение оператора & массивов NumPy.


операции со списком Python работают на список. list1 and list2 будет проверить, если list1 пусто, и return list1 если это так, и list2 если это не так. list1 + list2 добавляет list2 до list1, поэтому вы получаете новый список с len(list1) + len(list2) элементы.

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

массивы Numpy поддержка элемент-мудрый операции. array1 & array2 будет вычислить побитовое или для каждого соответствующего элемента array1 и array2. array1 + array2 вычислит сумму для каждого соответствующего элемента в array1 и array2.

это не работает для and и or.

array1 and array2 по существу является короткой рукой для следующего кода:

if bool(array1):
    return array2
else:
    return array1

для этого вам нужно хорошее определение bool(array1). Для глобальных операций, таких как используется в списках Python, определение таково:bool(list) == True если list не пусто, а False если он пустой. Для операций numpy по элементам существует некоторая неясность, следует ли проверять, оценивает ли какой-либо элемент True, или все элементы оценки в True. Поскольку оба, возможно, правильны, numpy не догадывается и поднимает ValueError, когда bool() (косвенно)вызывается массивом.


для первого примера и базы на Джанго док
Он всегда будет возвращать второй список, действительно, непустой список рассматривается как истинное значение для Python, поэтому python возвращает "последнее" истинное значение, поэтому второй список

In [74]: mylist1 = [False]
In [75]: mylist2 = [False, True, False,  True, False]
In [76]: mylist1 and mylist2
Out[76]: [False, True, False, True, False]
In [77]: mylist2 and mylist1
Out[77]: [False]