Подтвердить что ты не робот

'и' (логическое) против '&' (поразрядно) - Почему разница в поведении со списками против массивов numpy?

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

Меня смущает правильное использование & и 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?

4b9b3361

Ответ 1

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 с &.

Ответ 2

Короткие замыкающие булевы операторы (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

О list

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

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

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

О and

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

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

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

Если a не отвечает, and спрашивает: какова ваша длина? (Опять же, вы можете настроить его как автора класса a). Если a отвечает 0, and делает то же самое, что и выше - считает false ( NOT 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. Он просто заботится о моем списке1, отвечая на вопросы протокола. И, конечно, mylist1 отвечает 5 на вопрос о длине, поэтому и возвращает mylist2. И это так. Он не имеет ничего общего с элементами mylist1 и mylist2 - они нигде не вводят изображение.

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

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

NumPy

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

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

НТН,

Ответ 4

Пример 1:

Вот как работают операторы и.

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

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

Пример 2:

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

>>> 3 & 1
1

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

>>> 0b0011 & 0b0001
0b0001

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

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

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

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

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

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

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

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

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

Пример 3 и 4:

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

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

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

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

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

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

Ответ 5

  • В 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 и оператора.

Ответ 6

Операции с списком 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 element-mise существует некоторая неоднозначность, следует ли проверять, оценивает ли какой-либо элемент True, или все элементы оценивают до True. Поскольку оба варианта являются правильными, numpy не угадывает и вызывает ValueError, когда bool() (косвенно) вызывает массив.

Ответ 7

Для первого примера и базы на django 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]