Разница между' и '(логическим) и ' & ' (побитовым) в 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 примера.

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

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

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

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

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

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

Numpy version 1.7.1

python 2.7

References all inline with text.

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

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

7 90

7 ответов:

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

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

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

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

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

объекты 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, кстати) можете быть полностью исключен, так как они не короткое замыкание. Они могут вернуть все, что угодно, когда переопределены, и 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 не может быть перегружен. Он является полностью общим и взаимодействует через определенный протокол. Что ты можете 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.массив не знает кто задает ему вопрос; он просто говорит о правде.)

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

HTH,

Пример 1:

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

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

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

пример 2:

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

>>> 3 & 1
1

это легче увидеть, что происходит с помощью двоичный литерал (те же цифры, что и выше):

>>> 0b0011 & 0b0001
0b0001

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

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

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

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

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

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

побитовое "и" этих двух утверждений немного более туманно:

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

если 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 и False".

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

операции со списком Python работают на список. list1 and list2 будет проверить, если list1 пусто, и возврата 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). Для глобальных операций, таких как использовать списки питона, определение заключается в том, что 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]