'and'(boolean)vs '&'(bitwise)-为什么列表和numpy数组在行为上有区别?

eni9jsuy  于 2023-05-22  发布在  其他
关注(0)|答案(8)|浏览(143)

如何解释列表与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!

This answerthis answer帮助我理解了and是布尔运算,而&是位运算。
我阅读了有关bitwise operations的内容以更好地理解这个概念,但我很难使用这些信息来理解我上面的4个例子。
示例4引导我得到了我想要的输出,所以这很好,但是我仍然对何时/如何/为什么应该使用and&感到困惑。为什么列表和NumPy数组在使用这些运算符时表现不同?

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

mm5n2pyu

mm5n2pyu1#

and测试两个表达式在逻辑上是否都是True,而&(当与True/False值一起使用时)测试两个表达式是否都是True
在Python中,空的内置对象通常在逻辑上被视为False,而非空的内置对象在逻辑上被视为True。这有助于常见的用例,即如果列表为空,则需要执行某些操作,如果列表不是空的,则需要执行其他操作。注意,这意味着列表[False]在逻辑上是True

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

因此在示例1中,第一个列表是非空的,因此逻辑上为True,因此and的真值与第二个列表的真值相同。(在我们的例子中,第二个列表是非空的,因此逻辑上是True,但是识别它需要不必要的计算步骤。
例如,列表不能以按位方式有意义地组合,因为它们可以包含任意不同的元素。可以按位组合的内容包括:真与假,整数。
相比之下,NumPy对象支持矢量化计算。也就是说,它们允许您对多个数据片段执行相同的操作。
示例3失败,因为NumPy数组(长度> 1)没有真值,因为这会防止基于向量的逻辑混淆。
示例4简单地是向量化位and操作。

底线

  • 如果您不处理数组,也不执行整数的数学操作,则可能需要and
  • 如果你有真值的向量,你想合并,使用numpy&
5jvtdoz2

5jvtdoz22#

关于list

首先是非常重要的一点,一切都将遵循(我希望)。
在普通的Python中,list在任何方面都没有什么特别之处(除了有一个可爱的构造语法,这主要是一个历史性的意外)。一旦创建了一个列表[3,2,6],它就只是一个普通的Python对象,就像数字3、集合{3,7}或函数lambda x: x+5一样。
(Yes,它支持更改元素,支持迭代,以及其他许多东西,但这就是类型:它支持一些操作,而不支持另一些操作。int支持提升到幂,但这并不使它变得非常特殊-它只是int的一部分。lambda支持调用,但这并不意味着它很特别--毕竟,这就是lambda的用途:)。

关于and

and不是一个操作符(你可以称之为“operator”,但你也可以称之为“for”操作符:)。Python中的运算符是(通过)在某种类型的对象上调用的方法,通常作为该类型的一部分编写。一个方法没有办法保存它的一些操作数的求值,但是and可以(并且必须)这样做。
这样做的结果是and不能重载,就像for不能重载一样。它是完全通用的,并通过指定的协议进行通信。你能做的是定制你的那部分协议,但这并不意味着你可以完全改变and的行为。方案为:
想象一下Python解释“a和B”(实际上并不是这样,但它有助于理解)。当涉及到“and”时,它会查看它刚刚评估的对象(a),并询问它:是真的吗?(NOT:你是True吗?)如果您是a的类的作者,您可以自定义此答案。如果a回答“否”,and(完全跳过B,它根本不被评估,并且)说:a是我的结果(不是:假的是我的结果)。
如果a没有回答,and会询问它:您的长度是多少?(同样,您可以作为a类的作者来定制这个)。如果a的答案是0,and的操作与上面的相同-认为它为false(NOTFalse),跳过b,并给出a作为结果。
如果a对第二个问题(“你的长度是多少”)的回答不是0,或者根本没有回答,或者对第一个问题(“你是真的吗”)的回答是“是”,and计算B,并说:b是我的结果。请注意,它不会***询问b任何问题。
另一种说法是a and b几乎与b if a else a相同,除了a只计算一次。
现在拿着笔和纸坐几分钟,说服自己当{a,B}是{True,False}的子集时,它的工作方式与布尔运算符的工作方式完全相同。但我希望我已经说服你们,它更通用,正如你们将看到的,这种方式更有用。

把这两个放在一起

现在我希望你能理解你的例子1。and不关心mylist 1是数字、列表、lambda还是Argmhbl类的对象。它只关心mylist 1对协议问题的回答。当然,对于长度问题,mylist 1的答案是5,所以和返回mylist 2。就是这样它与mylist 1和mylist 2的元素没有任何关系--它们不会进入图片的任何地方。

示例二:list上的&

另一方面,&是一个像任何其他运算符一样的运算符,例如+。可以通过在该类上定义一个特殊的方法来为该类型定义它。int将其定义为按位的“and”,bool将其定义为逻辑的“and”,但这只是一个选项:例如,集合和一些其他对象(如Dict键视图)将其定义为集合交集。list只是没有定义它,可能是因为Guido没有想到任何明显的定义方法。

numpy

另一方面:-D,numpy数组 * 是 * 特殊的,或者至少他们试图成为。当然,numpy.array只是一个类,它不能以任何方式覆盖and,所以它做了下一个最好的事情:当被问到“你是真的吗”时,numpy.array会引发ValueError,实际上是说“请重新表述这个问题,我对真理的看法不适合你的模型”。(注意ValueError消息并没有提到and--因为numpy.array不知道 * 谁 * 在问它这个问题;它只讲真理)。
对于&,情况完全不同。numpy.array可以按照自己的意愿定义它,并且它定义的&与其他运算符一致:所以你最终得到了你想要的。
HTH,

9avjhtql

9avjhtql3#

短路布尔运算符(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__),因此您可以影响返回的操作数,但不会返回不同的内容。列表(和其他集合)被定义为当它们包含任何东西时是“truthy”,当它们为空时是“falsey”。
NumPy数组拒绝这个概念:对于他们所针对的用例,两种不同的真理概念是常见的:(1)是否有任何元素为真,以及(2)是否所有元素都为真。由于这两个完全(和无声)不兼容,并且没有一个明显更正确或更常见,NumPy拒绝猜测并要求您显式使用.any().all()
&|(顺便说一句,还有not)* 可以 * 被完全覆盖,因为它们不会短路。它们在被覆盖时可以返回任何东西,NumPy很好地利用了这一点来进行元素操作,就像它们实际上对任何其他标量操作所做的那样。另一方面,列表不会在它们的元素之间传播操作。就像mylist1 - mylist2没有任何意义,mylist1 + mylist2的意义完全不同一样,列表也没有&运算符。

afdcj2ne

afdcj2ne4#

示例1:

这就是and运算符的工作方式。

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

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

示例二:

&运算符用于按位的and,正如你提到的。按位运算只对数字起作用。a & B 的结果是由在 ab 中均为1的位中的1组成的数。例如:

>>> 3 & 1
1

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

>>> 0b0011 & 0b0001
0b0001

按位运算在概念上类似于布尔(真值)运算,但它们只对位起作用。
根据我的车的几份证词
1.我的车是红色的
1.我的车有轮子
这两个语句的逻辑“与”是:
(is我的车是红色的?)和(汽车有轮子吗?)=> logical true of false value
这两个都是真的,至少对我的车来说是这样。因此,整个语句的值在逻辑上是真的。
这两个语句的按位“与”有点模糊:
(the语句'my car is red'的数值)&(语句'my car has wheels'的数值)=> number
如果python知道如何将语句转换为数值,那么它将这样做并计算两个值的按位与。这可能会让你认为&and是可以互换的,但是和上面的例子一样,它们是不同的东西。此外,对于无法转换的对象,您将仅获得TypeError

实施例3和4:

Numpy实现了数组的算术运算:
ndarray上的算术和比较操作被定义为逐元素操作,并且通常产生ndarray对象作为结果。
但不实现数组的逻辑操作,因为你can't overload logical operators in python。这就是为什么示例3不起作用,但示例4起作用。
下面是and&的比较:使用and
按位操作用于检查数字的结构(设置了哪些位,未设置哪些位)。这类信息主要用于低级操作系统接口(例如unix permission bits)。大多数python程序不需要知道这些。
然而,逻辑运算(andornot)一直在使用。

kx7yvsdv

kx7yvsdv5#

1.在Python中,X and Y的表达式返回Y,假设bool(X) == TrueXY中的任何一个的计算结果为False,例如:

True and 20 
>>> 20

False and 20
>>> False

20 and []
>>> []

1.按位运算符根本没有为列表定义。但它是为整数定义的-对数字的二进制表示进行操作。考虑16(01000)和31(11111):

16 & 31
>>> 16
  1. NumPy不是灵媒,它不知道,你是否意味着例如。在逻辑表达式中,[False, False]应该等于True。在这里,它覆盖了一个标准的Python行为,即:任何带有len(collection) == 0的空集合都是False
    1.可能是NumPy数组的&运算符的预期行为。
jchrr9hc

jchrr9hc6#

对于第一个例子,基于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]
4zcjmb1e

4zcjmb1e7#

使用Python list的操作在 list 上操作。list1 and list2将检查list1是否为空,如果为空则返回list1,如果为空则返回list2list1 + list2将把list2追加到list1,因此您将获得一个包含len(list1) + len(list2)元素的新列表。
只有在按元素应用时才有意义的运算符(如&)会引发TypeError,因为如果不循环元素,则不支持按元素操作。
Numpy数组支持 element-wise 操作。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

nvbavucw

nvbavucw8#

问得好与您对逻辑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,如sum(mat),仅沿着轴求和。参见下文:

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

相关问题