Чем объясняется разница в поведении логических и побитовых операций над списками и массивами 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 would have expected [False, True, False, False, False]
# ---- Example 2 ----
>>> mylist1 & mylist2
TypeError: unsupported operand type(s) for &: 'list' and 'list'
# Why not just like example 1?
>>> import numpy as np
# ---- Example 3 ----
>>> 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()
# 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!
Этот ответ и этот ответ помогли мне понять, что and
это логическая операция, но &
это побитовая операция.
Я читал о побитовых операциях, чтобы лучше понять концепцию, но изо всех сил пытаюсь использовать эту информацию, чтобы разобраться в моих приведенных выше 4 примерах.
Пример 4 привел меня к желаемому выходу, так что это хорошо, но я до сих пор путают о том, когда / как / почему я должен использовать and
против &
. Почему списки и массивы NumPy по-разному работают с этими операторами?
Может ли кто-нибудь помочь мне понять разницу между логическими и побитовыми операциями, чтобы объяснить, почему они по-разному обрабатывают списки и массивы NumPy?
np.bitwise_and()
иnp.logical_and()
и друзей , чтобы избежать путаницы.mylist1 and mylist2
не выводится тот же результатmylist2 and mylist1
, что и, поскольку возвращается второй список, указанный delnan.[a and b for a, b in zip(mylist1, mylist2)]
Ответы:
and
проверяет, являются ли оба выражения логическими, вTrue
то время как&
(при использовании сTrue
/False
values) проверяет, являются ли оба выражения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
это мой результат. Обратите внимание, что он НЕ задаетb
никаких вопросов.Другой способ сказать, что все это
a and b
почти то же самоеb if a else a
, за исключением того, что a оценивается только один раз.Теперь посидите несколько минут с ручкой и бумагой и убедитесь, что когда {a, b} является подмножеством {True, False}, это работает точно так, как вы ожидаете от логических операторов. Но я надеюсь, что убедил вас, что это гораздо более общий и, как вы увидите, гораздо более полезный способ.
Собираем эти двое вместе
Теперь я надеюсь, что вы понимаете ваш пример 1.
and
Мне все равно, является ли mylist1 числом, списком, лямбда-выражением или объектом класса Argmhbl. Его просто волнует ответ mylist1 на вопросы протокола. И, конечно же, mylist1 отвечает 5 на вопрос о длине, поэтому и возвращает mylist2. Вот и все. Это не имеет ничего общего с элементами mylist1 и mylist2 - они никуда не входят в картинку.Второй пример:
&
наlist
С другой стороны,
&
это такой же оператор, как и любой другой, например, как+
. Его можно определить для типа, определив специальный метод для этого класса.int
определяет его как побитовое «and», а bool определяет его как логическое «and», но это только один вариант: например, наборы и некоторые другие объекты, такие как представления ключей dict, определяют его как пересечение наборов.list
просто не дает ему определения, вероятно, потому, что Гвидо не придумал никакого очевидного способа его определения.тупой
На другой ноге: -D, Numpy массивы являются специальными, или , по крайней мере , они пытаются быть. Конечно, numpy.array - это просто класс, он не может переопределить
and
каким-либо образом, поэтому он делает следующее лучшее: когда его спрашивают «правда ли?», Numpy.array вызывает ValueError, эффективно говоря: «Пожалуйста, перефразируйте вопрос, мой взгляд на истину не вписывается в вашу модель ». (Обратите внимание, что сообщение ValueError не говорит оand
- потому что numpy.array не знает, кто задает ему вопрос; он просто говорит об истине.)Ведь
&
это совсем другая история. numpy.array может определять его по своему усмотрению, и он определяется&
согласованно с другими операторами: точечно. Итак, вы наконец получили то, что хотите.HTH,
источник
Закорачивающие логические операторы (
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
означает что-то совершенно другое,&
для списков нет оператора.источник
[False] or [True]
вычисление[False]
и[False] and [True]
вычисление[True]
.Пример 1:
Так работает оператор и .
x и y => если x ложно, то x , иначе y
Другими словами, поскольку
mylist1
это не такFalse
, результатом выражения являетсяmylist2
. (Только пустые списки оцениваютсяFalse
.)Пример 2:
&
Оператор для побитового и, как вы говорите. Побитовые операции работают только с числами. Результатом a & b является число, состоящее из единиц в битах, которые равны 1 как в a, так и в b . Например:>>> 3 & 1 1
Проще увидеть, что происходит, используя двоичный литерал (те же числа, что и выше):
>>> 0b0011 & 0b0001 0b0001
Поразрядные операции похожи по концепции на логические (истинные) операции, но работают только с битами.
Итак, учитывая пару утверждений о моей машине
Логическое «и» этих двух утверждений:
И то, и другое верно, по крайней мере, для моей машины. Так что значение утверждения в целом логически верно.
Побитовое «и» этих двух операторов немного более расплывчато:
Если python знает, как преобразовать операторы в числовые значения, он сделает это и вычислит побитовое и двух значений. Это может привести вас к мысли, что
&
это взаимозаменяемоеand
, но, как и в приведенном выше примере, это разные вещи. Кроме того, для объектов, которые нельзя преобразовать, вы просто получите расширениеTypeError
.Пример 3 и 4:
Numpy реализует арифметические операции для массивов:
Но не реализует логические операции для массивов, потому что вы не можете перегружать логические операторы в python . Вот почему пример 3 не работает, а пример 4 работает.
Итак, чтобы ответить на ваш вопрос
and
vs&
: используйтеand
.Поразрядные операции используются для проверки структуры числа (какие биты установлены, какие не установлены). Такая информация в основном используется в интерфейсах низкоуровневой операционной системы ( например, биты разрешений unix ). Большинству программ на Python это не нужно знать.
Логические операции (
and
,or
,not
), однако, используются все время.источник
В 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
isFalse
».Вероятно, ожидаемое поведение оператора & массивов NumPy.
источник
Для первого примера и на основе документа django
Он всегда будет возвращать второй список, действительно, непустой список рассматривается как значение 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)
элементами.Операторы, которые имеют смысл только при поэлементном применении, например
&
, raise aTypeError
, поскольку поэлементные операции не поддерживаются без цикла по элементам.Массивы 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
iflist
не пусто, аFalse
если оно пусто. Для поэлементных операций numpy существует некоторая неоднозначность, следует ли проверять, оценивается ли какой-либо элементTrue
или все элементы оцениваютсяTrue
. Поскольку оба, возможно, верны, numpy не угадывает и вызывает,ValueError
когдаbool()
(косвенно) вызывается для массива.источник
Хороший вопрос. Подобно вашему наблюдению о примерах 1 и 4 (или я должен сказать 1 и 4 :)) над логическими
and
побитовыми&
операторами, я испытал наsum
операторе. Numpysum
и py также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])
источник