‘ and’(boolean) vs’&’(bitwise)-为什么列表和数字数组的行为有所不同?

是什么解释了列表上的布尔运算和按位运算与 NumPy 数组在行为上的不同呢?

我对在 Python 中正确使用 &and感到困惑,如下面的示例所示。

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 数组的方式不同吗?

119304 次浏览
  1. 在 Python 中,如果 bool(X) == True或任何 XY的值为 False,则 X and Y的表达式返回 Y,例如:

    True and 20
    >>> 20
    
    
    False and 20
    >>> False
    
    
    20 and []
    >>> []
    
  2. Bitwise operator is simply not defined for lists. But it is defined for integers - operating over the binary representation of the numbers. Consider 16 (01000) and 31 (11111):

    16 & 31
    >>> 16
    
  3. NumPy is not a psychic, it does not know, whether you mean that e.g. [False, False] should be equal to True in a logical expression. In this it overrides a standard Python behaviour, which is: "Any empty collection with len(collection) == 0 is False".

  4. Probably an expected behaviour of NumPy's arrays's & operator.

and测试两个表达式在逻辑上是否都是 True,而 &(与 True/False值一起使用时)测试两个表达式是否都是 True

在 Python 中,空的内置对象通常在逻辑上被视为 False,而非空的内置对象在逻辑上被视为 True。这有利于在列表为空时执行某些操作,而在列表不为空时执行其他操作的常见用例。注意,这意味着列表[ False ]逻辑上是 True:

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

因此在例1中,第一个列表是非空的,因此逻辑上是 True,所以 and的真值与第二个列表的真值相同。(在我们的例子中,第二个列表是非空的,因此逻辑上是 True,但是识别它将需要不必要的计算步骤。)

例如2,列表不能以位方式进行有意义的组合,因为它们可以包含任意的异类元素。可以按位组合的内容包括: true 和 Falses、整数。

相比之下,NumPy 对象支持向量化计算。也就是说,它们允许您对多个数据片段执行相同的操作。

示例3失败是因为 NumPy 数组(长度 > 1)没有真值,因为这可以防止基于向量的逻辑混淆。

示例4只是一个向量化的位 and操作。

底线

  • 如果您不处理数组,也不执行整数的数学操作,那么您可能需要 and

  • 如果您有希望组合的真值向量,请使用 numpy&

短路布尔运算符(andor)不能被覆盖,因为在不引入新的语言特性或牺牲短路的情况下,没有令人满意的方法来做到这一点。你可能知道,也可能不知道,它们计算第一个操作数的真值,根据这个值,要么计算并返回第二个参数,要么不计算第二个参数并返回第一个:

something_true and x -> x
something_false and x -> something_false
something_true or x -> something_true
something_false or x -> x

注意,返回的是(计算)实际操作数的结果,而不是它的真值。

自定义其行为的唯一方法是覆盖 __nonzero__(在 Python 3中重命名为 __bool__) ,这样就可以影响返回哪个操作数,但不会返回不同的内容。当列表(和其他集合)包含任何内容时,它们被定义为“真实的”,当它们为空时,它们被定义为“假的”。

NumPy 数组拒绝这个概念: 对于它们所针对的用例,有两个不同的真值概念是常见的: (1)是否有任何元素为真,(2)是否所有元素为真。由于这两者是完全(且无声地)不兼容的,而且两者都不明显更正确或更常见,NumPy 拒绝猜测,并要求您显式地使用 .any().all()

&|(顺便说一下,还有 not) 可以被完全覆盖,因为它们不会短路。当被重写时,它们可以返回任何内容,NumPy 可以很好地利用这一点来执行元素方面的操作,就像它们对实际上任何其他标量操作所做的那样。另一方面,列表不会在它们的元素之间广播操作。正如 mylist1 - mylist2没有任何含义,mylist1 + mylist2的含义完全不同,对于列表没有 &操作符。

使用 Python 列表对 名单进行操作。list1 and list2将检查 list1是否为空,如果为空则返回 list1,如果为空则返回 list2list1 + list2将向 list1追加 list2,因此您将获得一个包含 len(list1) + len(list2)元素的新列表。

只有在按元素方式应用时才有意义的操作符(如 &)引发 TypeError,因为如果不循环遍历元素,就不支持按元素方式操作。

Numpy 数组支持 元素方面操作。array1 & array2将按位计算或对 array1array2中的每个对应元素计算。array1 + array2将计算 array1array2中每个对应元素的和。

这不适用于 andor

array1 and array2实际上是以下代码的简写:

if bool(array1):
return array2
else:
return array1

为此,你需要一个良好的定义 bool(array1)。对于 Python 列表中使用的全局操作,定义是如果 list不为空则为 bool(list) == True,如果为空则为 False。对于 numpy 的元素方面的操作,是检查任何元素的值是否为 True,还是检查所有元素的值是否为 True,存在一些不确定性。因为两者都可以证明是正确的,所以 numpy 不会猜测,而是在(间接地)对数组调用 bool()时引发 ValueError

例子一:

这就是 还有操作员的工作方式。

X = > 如果 X为 false,则 X,else < em > y

换句话说,因为 mylist1不是 False,所以表达式的结果是 mylist2。(只有 空名单评价为 False。)

例二:

&操作符是位操作符,正如您所提到的。按位运算只对数字有效。B的结果是一个由1组成的数字,在 B中都是1。例如:

>>> 3 & 1
1

使用 二进制文字(与上面的数字相同)更容易看到发生了什么:

>>> 0b0011 & 0b0001
0b0001

按位运算在概念上类似于布尔(真值)运算,但它们只对位运算有效。

根据我对我车子的陈述

  1. 我的车是红色的
  2. 我的车有轮子

这两句话的逻辑“和”是:

(我的车是红色的吗?)和(汽车有轮子吗?) = > 逻辑真的假值

这两个都是真的,至少对我的车来说。所以整个语句的值是 逻辑上来说 true。

这两个语句中的按位“ and”更模糊一些:

(语句‘ my car is red’的数值) & (语句‘ my car has Wheels’的数值) = > number

如果 python 知道如何将语句转换为数值,那么它将这样做,并计算两个值的位和位。这可能会让你相信 &and是可互换的,但是和上面的例子一样,它们是不同的东西。而且,对于无法转换的对象,您只能得到一个 TypeError

例三及例四:

Numpy 为数组实现了 算术运算:

在 ndarray 上的算术和比较操作被定义为元素方面的操作,并且通常产生 ndarray 对象作为结果。

但是不实现数组的逻辑操作,因为您使用的是 不能在 python 中重载逻辑运算符。这就是为什么例三不起作用,但是例四起作用。

因此,要回答你的 and&的问题: 使用 and

按位操作用于检查数字的结构(设置了哪些位,哪些位没有设置)。此类信息主要用于低级操作系统接口(例如 Unix 权限位)。大多数 python 程序不需要知道这一点。

然而,逻辑操作(andornot)一直在使用。

对于第一个例子和基础上的 姜戈的医生
它总是返回第二个列表,实际上一个非空列表被看作 Python 的 True 值,因此 Python 返回‘ last’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]

关于 list

首先是一个非常重要的观点,我希望从这一点出发,一切都会顺理成章。

在普通的 Python 中,list在任何方面都没有特别之处(除了具有可爱的构造语法,这主要是一个历史意外)。一旦创建了一个列表 [3,2,6],它就只是一个普通的 Python 对象,如数字 3、 set {3,7}或函数 lambda x: x+5

(是的,它支持更改它的元素,它支持迭代和许多其他的东西,但是这就是类型: 它支持一些操作,而不支持其他一些操作。Int 支持上升为幂,但是这并没有使它变得非常特殊——它只是 int 的一种形式。Lambda 支持调用,但这并没有让它变得非常特殊——毕竟,这就是 lambda 的用途:)。

关于 and

and不是一个操作员(你可以称它为“操作员”,但是你也可以称它为“ for”一个操作员:)。Python 中的运算符是(通过)对某种类型的对象调用的方法,通常作为该类型的一部分编写。方法没有办法保存它的某些操作数的计算结果,但是 and可以(也必须)这样做。

其结果是 and不能被重载,就像 for不能被重载一样。它是完全通用的,并通过指定的协议进行通信。可以所做的是自定义协议的一部分,但这并不意味着您可以完全改变 and的行为。协议是:

想象一下 Python 解释“ a 和 b”(字面上不是这样的,但它有助于理解)。当提到“ and”时,它会查看它刚刚计算过的对象(a) ,然后问它: 你是真的吗?(你是 True吗?)如果您是某个类的作者,则可以自定义此答案。如果 a回答“ no”,and(完全跳过 b,根本不计算它)说: a是我的结果(没有: False 是我的结果)。

如果 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是数字、 list、 lambda 还是 Argmhbl 类的对象。它只关心 mylist1对协议问题的回答。当然,mylist1回答了关于长度的问题5,因此返回 mylist2。就是这样。它与 mylist1和 mylist2的元素没有任何关系——它们不会进入图片的任何位置。

第二个例子: list上的 &

另一方面,&与其他操作符一样,是一个操作符,例如 +。可以通过在类上定义一个特殊的方法来为类定义它。int将其定义为按位“ and”,而 bool 将其定义为逻辑“ and”,但这只是一个选项: 例如,set 和其他一些对象(如 dictkey 视图)将其定义为 set 交集。list只是没有定义它,可能是因为圭多没有想到任何明显的方式来定义它。

麻木不仁

另一方面: -D,特殊的数组 ,或者至少它们正在尝试这样做。当然,numpy.array 只是一个类,它不能以任何方式覆盖 and,所以它做了次好的事情: 当被问到“你是真的吗”时,numpy.array 会引发一个 ValueError,实际上是说“请重新措辞这个问题,我对真相的看法不适合你的模型”。(注意,ValueError 消息没有提到 and——因为 numpy.array 不知道 正在问它这个问题; 它只是提到了真理。)

对于 &来说,情况完全不同。Array 可以根据自己的意愿定义它,并且它与其他操作符一致地定义 &: 逐点定义。你终于得到你想要的了。

HTH,

问得好。与逻辑 and&运算符上的示例1和示例4(或者我应该说1和4:)类似,我在 sum运算符上体验到了类似的情况。麻木的 sum和 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 (如 sum (mat))仅沿着轴进行总计。见下文:

np.sum(mat)  ## --> gives 325
sum(mat)     ## --> gives array([55, 60, 65, 70, 75])