2014-03-25 3 views
71

Что объясняет разницу в поведении булевых и побитовых операций над списками vs numpy.arrays?Разница между 'и' (boolean) против '&' (побитовая) в python. Почему разница в поведении со списками против массивов 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! 

Это answer, и это answer и помог мне понять, что 'and' является булевой операции, но '&' является операция поразрядного.

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

Обратите внимание, в моей конкретной ситуации, мой желаемый результат является NewList где:

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

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

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

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

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

Numpy version 1.7.1 

python 2.7 

References all inline with text. 

редактирует

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

+4

Пример 1 только дает правильный результат. На самом деле он просто возвращает второй список без изменений. Попробуйте другие списки, в частности, все, где во втором списке содержится «True» в позиции «False» в первом списке: логическая логика диктует вывод «False» в этой позиции, но вы получите «True» , – delnan

+0

@delnan Спасибо, что заметили двусмысленность в моих примерах. Я обновил свои примеры, чтобы подчеркнуть мое замешательство и сосредоточиться на аспекте этого поведения, которое я не понимаю. Мне явно не хватает чего-то важного, потому что я не ожидал вывода из примера 1. – rysqui

+2

В Numpy есть '' np.bitwise_and() '' и '' np.logical_and() '' и друзья, чтобы избежать путаницы. – Dietrich

ответ

63

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

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

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

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

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

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

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

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

Bottom Line

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

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

7
  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 равна False».

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

+0

'False и 20' возвращает' False', просто если быть точным .. – aebmad

+0

False и 20 возвращает False – Rahul

18

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

+3

Одним из особенно интересных примеров того, что это может произвести, является '[False] или [True]' оценивается как '[False]', а '[False] и [True]' оценивает на '[True]'. –

+0

Отличный ответ, спасибо! – Kevin

4

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

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

Поддержка массивных массивов element-wise операции. 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() (косвенно) вызывает массив.

8

Пример 1:

Вот как and работает оператор в.

х и у => если х ложно, то х, еще у

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

Пример 2:

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

>>> 3 & 1 
1 

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

>>> 0b0011 & 0b0001 
0b0001 

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

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

  1. Мой автомобиль красный
  2. Мой автомобиль имеет колеса

Логическое «и» этих двух утверждений:

(это мой автомобиль красный?) И (у автомобиля есть колеса?) => Логическое значение ложного значения

Оба варианта верны для моей машины. Таким образом, значение оператора в целом равно логически true.

поразрядное «и» этих двух утверждений является немного более туманной:

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

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

Пример 3 и 4:

Numpy реализует arithmetic operations для массивов:

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

Но не реализует логические операции для массивов, потому что вы can't overload logical operators in python. Вот почему пример три не работает, но пример четыре.

Чтобы ответить на ваш вопрос and vs & вопрос: используйте and.

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

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

3

Для первого примера и баз на django's doc
Он всегда будет возвращать второй список, действительно не пустой список см как истинное значение для 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] 
14

О list

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

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

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

О and

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

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

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

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

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

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

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

Положив эти два вместе

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

Второй пример: & на list

С другой стороны, & оператор, как и любой другой, как +, например. Он может быть определен для типа, задав специальный метод для этого класса. int определяет его как поразрядное «и», а bool определяет его как логическое «и», но это всего лишь один вариант: например, множества и некоторые другие объекты, такие как представления ключей ключей, определяют его как заданное пересечение. list просто не определяет его, вероятно, потому, что Гвидо не думал о каком-либо очевидном способе его определения.

NumPy

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

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

HTH,