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


141

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

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

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

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


2
在Numpy中,有np.bitwise_and()np.logical_and()和朋友,以避免混淆。
Dietrich

1
在示例1中,mylist1 and mylist2不会输出与相同的结果mylist2 and mylist1,因为返回的是delnan指出的第二个列表。
user2015487 '16

Answers:


113

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&


26

关于 list

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

在普通的Python中,list它在任何方面都不是特殊的(除了具有可爱的构造语法,这主要是历史性的事故)。一旦创建了列表[3,2,6],它就具有所有意图和用途,只是一个普通的Python对象,例如number 3,set {3,7}或function lambda x: x+5

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

关于 and

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

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

想象一下,Python解释“ a和b”(这种方式在字面上并没有发生,但有助于理解)。当涉及“和”时,它查看刚刚评估过的对象(a),并询问:您是真的吗?(不是:是吗True?)如果您是班级的作者,则可以自定义此答案。如果a答案为“否”,and(完全跳过b,则完全不评估,并且)说:a是我的结果(不是:我的结果是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是数字,列表,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,


23

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

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
关于可以产生的一个特别有趣的例子是[False] or [True]评估[False],并[False] and [True]评估为[True]
罗伯·瓦茨

16

范例1:

运算符就是这样工作的。

xy =>如果x为false,则为x,否则为y

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

范例2:

&操作是按位,正如你提到。按位运算仅适用于数字。ab的结果是一个由1组成的数字,而ab均为1 。例如:

>>> 3 & 1
1

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

>>> 0b0011 & 0b0001
0b0001

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

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

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

这两个语句的逻辑“和”为:

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

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

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

(语句“我的车是红色的”的数值)和(语句“我的车有轮子的”的数值)=>数字

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

示例3和4:

Numpy实现数组的算术运算

ndarray上的算术和比较运算被定义为逐元素运算,通常将ndarray对象作为结果。

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

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

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

逻辑运算(andornot),然而,使用所有的时间。


14
  1. 在Python的表达X and Y回报Y,鉴于bool(X) == True或任何的XY评估为False,例如:

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

    16 & 31
    >>> 16
  3. NumPy不是通灵的,它不知道您是否意味着例如在逻辑表达式中[False, False]应该等于True。在此方法中,它替代了标准的Python行为,即:“任何带有len(collection) == 0is的空集合False”。

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


错误和20返回错误
Rahul

4

对于第一个示例,基于Django的文档
,它将始终返回第二个列表,实际上非空列表被视为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]

4

使用Python列表的操作在列表上进行list1 and list2将检查是否list1为空,并返回list1,如果它是,并且list2如果它不是。list1 + list2将附加list2list1,因此您将获得一个包含len(list1) + len(list2)元素的新列表。

仅在元素级应用时有意义的运算符(例如&)会引发一个TypeError,因为不支持在元素之间循环而不进行元素级操作。

Numpy数组支持按元素操作。array1 & array2将计算的按位或用于在每个相应元件array1array2array1 + array2将计算在每个相应元素的总和array1array2

这不适用于andor

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

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

为此,您需要对进行良好的定义bool(array1)。对于像在Python列表上使用的全局操作,其定义是bool(list) == Trueif list不为空,并且False为空。对于numpy的逐元素运算,是否要检查是否有任何元素求值为True,还是要检查所有元素的取值为,都存在一些歧义True。因为两者都可以说是正确的,所以numpy不会猜测并引发(间接)在数组上调用ValueErrorwhen的bool()情况。


0

好问题。与您在逻辑and按位运算&符上对示例1和4(或者我应该说1&4 :)的观察类似,我对运算符也有经验sum。numpy sum和py的sum行为也不同。例如:

假设“ mat”是一个numpy 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])
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.