关于python:’and'(布尔)vs’&'(按位)-为什么列表与numpy数组在行为上有所不同?

'and' (boolean) vs '&' (bitwise) - Why difference in behavior with lists vs numpy arrays?

是什么解释了列表和NumPy数组上布尔运算和按位运算的行为差异?

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

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
26
27
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使我达到所需的输出,这很好,但是对于何时/如何/为什么应该使用and vs &,我仍然感到困惑。 为什么列表和NumPy数组在这些运算符上的行为不同?

谁能帮助我了解布尔运算和按位运算之间的区别,以解释为什么它们对列表和NumPy数组的处理方式不同?


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

在Python中,空的内置对象通常在逻辑上被视为False,而非空的内置对象在逻辑上被视为True。这可以简化常见的用例,在这种情况下,如果列表为空,则要执行某项操作;如果列表为空,则要执行其他操作。请注意,这意味着列表[False]在逻辑上是True

1
2
3
4
>>> if [False]:
...    print 'True'
...
True

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

例如,2不能以位方式有意义地组合列表,因为它们可以包含任意不同的元素。可以按位组合的事物包括:对和错,整数。

相反,NumPy对象支持矢量化计算。也就是说,它们使您可以对多个数据执行相同的操作。

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

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

底线

  • 如果您不处理数组并且不执行整数的数学运算,则可能需要and

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


短路布尔运算符(andor)不能被覆盖,因为没有令人满意的方法就可以做到这一点,而又不会引入新的语言功能或牺牲短路。如您所知,您可能不知道,他们评估第一个操作数的真值,并根据该值评估或返回第二个参数,或者不评估第二个参数而返回第一个:

1
2
3
4
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表示完全不同的东西一样,列表没有&运算符。


关于list

首先是非常重要的一点,一切都会随之而来(我希望)。

好。

在普通的Python中,list在任何方面都不是特殊的(除了具有可爱的构造语法,这主要是历史性的事故)。创建列表[3,2,6]后,就所有目的和目的而言,它只是一个普通的Python对象,例如数字3,集合{3,7}或函数lambda x: x+5

好。

(是的,它支持更改其元素,并支持迭代和许多其他功能,但这就是类型的含义:它支持某些操作,而不支持其他一些操作。int支持提升为幂,但不支持此功能。让它变得非常特别-这就是一个int的意思。lambda支持调用,但这并没有使其非常特别-这毕竟是lambda的目的:)。

好。

关于and

and不是运算符(您可以将其称为" operator",但也可以将" for"称为运算符:)。 Python中的运算符是通过某种类型的对象(通常作为该类型的一部分编写)调用的方法(通过实现)。一种方法无法对其某些操作数进行求值,但是and可以(必须)这样做。

好。

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

好。

想象一下,Python解释" a和b"(这种方式在字面上并不是真的,但是有助于理解)。当涉及到"和"时,它查看刚刚评估的对象(a),并询问:您是真的吗? (不是:您True吗?)如果您是班级的作者,则可以定制此答案。如果a回答"否",则and(完全跳过b,完全不求值,并且)说:a是我的结果(不是:False是我的结果)。

好。

如果a没有回答,则and会问:您的长度是多少? (再次,您可以将其自定义为a的类的作者)。如果a回答为0,则and的操作与上述相同-认为它为false(否False),跳过b,并给出a作为结果。

好。

如果a对第二个问题("您的长度是多少")回答的不是0,或者根本不回答,或者对第一个问题的回答是"是"("您是真的"),< x6>评估b,并说:b是我的结果。请注意,它不会问b任何问题。

好。

说所有这些的另一种方式是a and bb if a else a几乎相同,除了a仅被评估一次。

好。

现在,用笔和笔坐几分钟,让自己相信,当{a,b}是{True,False}的子集时,它的工作原理与布尔运算符所期望的完全一样。但是,我希望我已经说服了您,它更加通用,并且您将看到,这种方式更加有用。

好。

将这两个放在一起

现在,我希望您理解您的示例1。and不在乎mylist1是数字,列表,lambda还是Argmhbl类的对象。它只关心mylist1对协议问题的回答。当然,mylist1回答有关长度的问题5,因此返回mylist2。就是这样。它与mylist1和mylist2的元素无关-它们不会在任何地方输入图片。

好。

第二个示例:list上的&

另一方面,&是一个与其他运算符类似的运算符,例如+。可以通过在类上定义特殊方法来为该类型定义它。 int将其定义为按位"和",而bool将其定义为逻辑"和",但这只是一个选择:例如,集合和诸如dict键视图之类的其他对象将其定义为集合交集。 list只是没有定义它,可能是因为Guido没想到任何明显的定义方法。

好。

麻木

另一方面:-D,numpy数组很特殊,或者至少是他们想要的。当然,numpy.array只是一个类,它不能以任何方式覆盖and,因此它做的第二件事是:当被问及"您是真的"时,numpy.array会引发ValueError,实际上是在说"请改写问题,我对真理的看法不适合您的模型"。 (请注意,ValueError消息没有讲到and,因为numpy.array不知道是谁在问这个问题;它只是讲真话。)

好。

对于&,这是完全不同的故事。 numpy.array可以根据需要定义它,并且它与其他运算符一致地定义&:逐点。这样您终于得到了想要的东西。

好。

HTH,

好。

好。


范例1:

和运算符就是这样工作的。

x和y =>
如果x为假,则为x,否则为y

因此,换句话说,由于mylist1不是False,因此表达式的结果是mylist2。 (只有空列表的值为False。)

范例2:

如前所述,&运算符用于按位运算。按位运算仅适用于数字。 a&b的结果是一个由1组成的数字,而a和b均为1。例如:

1
2
>>> 3 & 1
1

使用二进制文字(上面的数字相同)更容易看到正在发生的事情:

1
2
>>> 0b0011 & 0b0001
0b0001

按位运算在概念上与布尔(真)运算相似,但是它们仅对位起作用。

所以,给我一些关于我的车的陈述

  • 我的车是红色的
  • 我的车有轮子
  • 这两个语句的逻辑"和"为:

    (is my car red?) and (does car have wheels?) => logical true of false value

    至少对于我的车来说,这两者都是正确的。因此,该语句的整体值在逻辑上是正确的。

    这两个语句的按位"和"有点模糊:

    (the numeric value of the statement 'my car is red') & (the numeric value of the statement 'my car has wheels') => number

    如果python知道如何将语句转换为数值,则它将这样做并计算两个值的按位和。这可能使您相信&and是可以互换的,但是与上面的示例一样,它们是不同的。另外,对于无法转换的对象,您只会得到TypeError

    示例3和4:

    Numpy实现数组的算术运算:

    Arithmetic and comparison operations on ndarrays are defined as element-wise operations, and generally yield ndarray objects as results.

    但是不会为数组实现逻辑运算,因为您无法在python中重载逻辑运算符。这就是为什么示例三不起作用,但是示例四却不起作用的原因。

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

    按位运算用于检查数字的结构(哪些位已设置,哪些位未设置)。这种信息主要用于底层操作系统接口(例如,unix许可位)。大多数python程序都不需要知道这一点。

    但是,逻辑运算(andornot)始终被使用。


  • 在Python中,如果bool(X) == TrueXY中的任何一个计算为False,则X and Y的表达式将返回Y,例如:

    1
    2
    3
    4
    5
    6
    7
    8
    True and 20
    >>> 20

    False and 20
    >>> False

    20 and []
    >>> []
  • 根本没有为列表定义按位运算符。但是它是为整数定义的-在数字的二进制表示形式上进行操作。考虑16(01000)和31(11111):

    1
    2
    16 & 31
    >>> 16
  • NumPy不是通灵的,它不知道,你的意思是
    例如在逻辑表达式中,[False, False]应等于True。在此方法中,它替代了标准的Python行为,即:"任何带有len(collection) == 0的空集合都是False"。

  • NumPy数组的&运算符的预期行为。


  • 对于第一个示例,基于Django的文档
    它将始终返回第二个列表,实际上,非空列表被视为Python的True值,因此python返回"最后一个" True值,因此第二个列表

    1
    2
    3
    4
    5
    6
    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为空,则返回list1,如果不是,则返回list2list1 + list2list2附加到list1,因此您将获得一个包含len(list1) + len(list2)元素的新列表。

    仅当按元素应用时有意义的运算符(例如&)会引发TypeError,因为不支持在不循环访问元素的情况下进行按元素的操作。

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

    这不适用于andor

    array1 and array2本质上是以下代码的简写:

    1
    2
    3
    4
    if bool(array1):
        return array2
    else:
        return array1

    为此,您需要一个很好的bool(array1)定义。对于像在Python列表上使用的全局操作,定义是bool(list) == True(如果list不为空)和False(如果为空)。对于numpy的按元素操作,是否检查任何元素的值为True还是所有元素的值为True都存在一些歧义。因为两者都是正确的,所以当(间接)在数组上调用bool()时,numpy不会猜测并引发ValueError