Разница между 'и' (логическим) и '&' (поразрядной) в питоне. Почему разница в поведении со списками и массивами?
Чем объясняется разница в поведении логических и побитовых операций над списками по сравнению с numpy.arrays?
Я запутался по поводу правильного использования&
"против"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.arrays?
Я просто хочу убедиться, что я продолжаю правильно использовать эти операции в будущем. Большое спасибо за помощь!
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, списки не могут быть осмысленно объединены побитовым способом, потому что они могут содержать произвольно отличающиеся элементы. Вещи, которые можно комбинировать побитово, включают в себя: Истины и Ложь, целые числа.
Объекты NumPy, напротив, поддерживают векторизованные вычисления. То есть они позволяют вам выполнять одни и те же операции над несколькими частями данных.
Пример 3 терпит неудачу, потому что массивы NumPy (длиной> 1) не имеют значения истинности, поскольку это предотвращает путаницу на основе векторной логики.
Пример 4 - это просто векторизованный бит and
операция.
Нижняя линия
Если вы не имеете дело с массивами и не выполняете математических манипуляций с целыми числами, вы, вероятно, захотите
and
,Если у вас есть векторы значений истинности, которые вы хотите объединить, используйте
numpy
с&
,
Около list
Сначала очень важный момент, из которого все будет следовать (я надеюсь).
В обычном Python, list
не является чем-то особенным (за исключением того, что у него есть симпатичный синтаксис для конструирования, что в основном является исторической случайностью) После списка [3,2,6]
сделано, это для всех намерений и целей просто обычный объект Python, как число 3
, задавать {3,7}
или функция lambda x: x+5
,
(Да, он поддерживает изменение своих элементов, поддерживает итерацию и многое другое, но это только один из типов: он поддерживает некоторые операции, но не поддерживает некоторые другие. Int поддерживает повышение до степени, но это не так сделать его очень особенным - это просто то, что является int. Лямбда поддерживает вызовы, но это не делает его особенным - это то, для чего, в конце концов, лямбда:).
Около and
and
не является оператором (вы можете назвать его "оператором", но вы также можете вызвать "для" оператора:). Операторы в Python - это (реализованные через) методы, вызываемые для объектов некоторого типа, обычно написанных как часть этого типа. Нет способа для метода провести оценку некоторых своих операндов, но and
может (и должен) сделать это.
Следствием этого является то, что and
не может быть перегружен, как for
не может быть перегружен. Он является полностью общим и связывается по указанному протоколу. Что вы можете сделать, это настроить свою часть протокола, но это не значит, что вы можете изменить поведение and
полностью. Протокол:
Представьте себе, что Python интерпретирует " a и b" (это не происходит буквально таким образом, но помогает пониманию). Когда дело доходит до "и", он смотрит на объект, который он только что оценил (а), и спрашивает его: ты прав? (НЕ: ты True
?) Если вы являетесь автором класса, вы можете настроить этот ответ. Если a
отвечает "нет", and
(полностью пропускает b, он вообще не оценивается, и) говорит: a
мой результат (НЕ: Ложь - мой результат).
Если a
не отвечает, and
спрашивает: какая у вас длина? (Опять же, вы можете настроить это как автор a
класс). Если a
ответы 0, and
делает то же, что и выше - считает это ложным (НЕ ложным), пропускает b и дает a
в результате.
Если a
отвечает на вопрос, отличный от 0, на второй вопрос ("какова ваша длина"), или он не отвечает вообще, или отвечает "да" на первый вопрос ("вы правы"), and
оценивает б и говорит: b
это мой результат. Обратите внимание, что это не спрашивает 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 определяет его как логическое "и", но это всего лишь один вариант: например, наборы и некоторые другие объекты, такие как представления ключей dict, определяют его как пересечение множеств. list
просто не определяет это, вероятно, потому что Гвидо не придумал никакого очевидного способа определить это.
NumPy
С другой стороны:-D, массивы numpy являются особыми, или, по крайней мере, они пытаются быть. Конечно, numpy.array - это просто класс, он не может переопределить and
в любом случае, поэтому он делает следующее лучшее: когда его спрашивают "ты прав", numpy.array вызывает ошибку ValueError, фактически говоря: "Пожалуйста, перефразируй вопрос, мой взгляд на истину не вписывается в твою модель". (Обратите внимание, что сообщение ValueError не говорит о and
- потому что numpy.array не знает, кто задает ему вопрос; это просто говорит об истине.)
За &
Это совсем другая история. numpy.array может определить его по своему желанию, и он определяет &
в соответствии с другими операторами: точечно. Итак, вы, наконец, получаете то, что хотите.
НТН,
Короткозамкнутые логические операторы (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
означает что-то совершенно другое, нет &
оператор для списков.
В Python выражение
X and Y
возвращаетсяY
, При условииbool(X) == True
или любой изX
или жеY
оценивать как False, например:True and 20 >>> 20 False and 20 >>> False 20 and [] >>> []
Побитовый оператор просто не определен для списков. Но он определен для целых чисел - работает над двоичным представлением чисел. Рассмотрим 16 (01000) и 31 (11111):
16 & 31 >>> 16
NumPy не экстрасенс, он не знает, имеете ли вы в виду, например,
[False, False]
должно быть равноTrue
в логическом выражении. В этом он переопределяет стандартное поведение Python, а именно: "Любая пустая коллекция сlen(collection) == 0
являетсяFalse
".Вероятно, ожидаемое поведение массивов & оператора NumPy.
Пример 1:
Так работает оператор and.
х и у => если х ложно, то х, иначе у
Иными словами, так как mylist1
не является False
, результат выражения mylist2
, (Только пустые списки оцениваются как False
.)
Пример 2:
&
Оператор для побитового и, как вы упоминаете. Побитовые операции работают только с числами. Результатом a & b является число, состоящее из 1 в битах, которые равны 1 как в a, так и в b. Например:
>>> 3 & 1
1
Проще увидеть, что происходит, используя двоичный литерал (те же числа, что и выше):
>>> 0b0011 & 0b0001
0b0001
Битовые операции в принципе похожи на логические (истинные) операции, но работают только с битами.
Итак, учитывая пару заявлений о моей машине
- Моя машина красная
- У моей машины есть колеса
Логическое "и" этих двух утверждений:
(моя машина красная?) и (у машины есть колеса?) => логическая истина ложного значения
И то, и другое верно, по крайней мере, для моей машины. Таким образом, ценность утверждения в целом логически верна.
Побитовое "и" этих двух утверждений немного более туманно:
(числовое значение оператора "у моей машины красный") & (числовое значение оператора "у моей машины есть колеса") => число
Если python знает, как преобразовать операторы в числовые значения, он сделает это и вычислит побитовое значение двух значений. Это может привести вас к мысли, что &
взаимозаменяем с and
, но, как и в приведенном выше примере, это разные вещи. Кроме того, для объектов, которые не могут быть преобразованы, вы просто получите TypeError
,
Пример 3 и 4:
Numpy реализует арифметические операции для массивов:
Арифметические операции и операции сравнения на ndarrays определяются как поэлементные операции и, как правило, приводят к ndarray-объектам в качестве результатов.
Но не реализует логические операции для массивов, потому что вы не можете перегружать логические операторы в Python. Вот почему пример три не работает, а пример четвертый.
Итак, чтобы ответить на ваш and
против &
вопрос: использовать and
,
Побитовые операции используются для проверки структуры числа (какие биты установлены, какие биты не установлены). Этот вид информации в основном используется в низкоуровневых интерфейсах операционной системы (например, биты разрешения Unix). Большинству программ на Python это не нужно знать.
Логические операции (and
, or
, not
), однако, используются все время.
Для первого примера и базы на django's doc
Он всегда будет возвращать второй список, на самом деле непустой список рассматривается как значение True для Python, таким образом, python возвращает "последнее" значение True, поэтому второй список
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]
Операции со списком 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)
, Для глобальных операций, которые используются в списках Python, определение таково, что bool(list) == True
если list
не пустой, а False
если он пуст. Для поэлементных операций numpy есть некоторая неоднозначность, проверять, оценивает ли какой-либо элемент True
или все элементы оцениваются как True
, Потому что оба, возможно, правильно, NumPy не догадывается и поднимает ValueError
когда bool()
(косвенно) вызывается для массива.
Хороший вопрос. Подобно вашему наблюдению о примерах 1 и 4 (или я должен сказать 1 и 4:)) над логическимиand
побитовый &
операторов, я испытал на sum
оператор. Тупицаsum
и ру sum
вести себя по-другому. Например:
Предположим, что "mat" представляет собой массив размером 5x5 2d, например:
array([[ 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 10],
[11, 12, 13, 14, 15],
[16, 17, 18, 19, 20],
[21, 22, 23, 24, 25]])
Тогда numpy.sum(mat) дает общую сумму всей матрицы. В то время как встроенная сумма из Python, такая как sum (mat), составляет только по оси. Смотри ниже:
np.sum(mat) ## --> gives 325
sum(mat) ## --> gives array([55, 60, 65, 70, 75])