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

2024-12-19 09:23:00
admin
原创
138
摘要:问题描述:如何解释列表和 NumPy 数组上的布尔和按位运算行为的差异? 我对Python 中&vs的适当用法感到困惑and,如以下示例所示。mylist1 = [True, True, True, False, True] mylist2 = [False, True, False, Tru...

问题描述:

如何解释列表和 NumPy 数组上的布尔和按位运算行为的差异?

我对Python 中&vs的适当用法感到困惑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 让我得到了我想要的输出,所以这很好,但我仍然对何时/如何/为什么应该使用andvs感到困惑&。为什么列表和 NumPy 数组在这些运算符下的行为不同?

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


解决方案 1:

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

在 Python 中,空的内置对象通常被视为逻辑上的,False而非空的内置对象在逻辑上是True。这方便了常见的用例,即如果列表为空,则执行某些操作,如果列表不为空,则执行其他操作。请注意,这意味着列表 [False] 在逻辑上是True

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

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

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

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

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

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

结论

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

  • 如果您有想要合并的真值向量,请使用numpywith &

解决方案 2:

关于list

首先有一个非常重要的观点,我希望一切都会由此而来。

在普通的 Python 中,list它并没有什么特别之处(除了构造语法很巧妙,这主要是历史偶然)。一旦[3,2,6]创建了列表,它就只是一个普通的 Python 对象,就像一个数字3、集合{3,7}或一个函数一样lambda x: x+5

(是的,它支持改变其元素,它支持迭代和许多其他事情,但这只是类型的本质:它支持某些操作,而不支持其他操作。int 支持提升幂,但这并没有什么特别之处 - 它只是一个 int。lambda 支持调用,但这并不意味着它很特别 - 毕竟,这就是 lambda 的用途:)。

关于and

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

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

想象一下 Python 解释“a and b”(这实际上不会以这种方式发生,但有助于理解)。当遇到“and”时,它会查看刚刚评估的对象(a),并询问它:你是真的吗?(不是:你是吗True?)如果您是 a 类的作者,您可以自定义此答案。如果a回答“否”,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 是数字、列表、lambda 还是 Argmhbl 类的对象。它只关心 mylist1 对协议问题的回答。当然,mylist1 对长度问题的回答是 5,所以返回 mylist2。就是这样。它与 mylist1 和 mylist2 的元素无关 - 它们不会出现在任何地方。

&第二个例子:list

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

numpy

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

对于&,情况则完全不同。numpy.array 可以按自己的意愿定义它,并且它&与其他运算符一致地定义:逐点。所以你最终会得到你想要的。

高血压,

解决方案 3:

短路布尔运算符 ( 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__(在 Python 3 中重命名为__bool__),这样您就可以影响返回哪个操作数,但不会返回其他内容。列表(和其他集合)在包含任何内容时定义为“真”,为空时定义为“假”。

NumPy 数组拒绝接受这种观点:对于它们所针对的用例,有两种不同的真值概念:(1) 任何元素是否为真,以及 (2) 所有元素是否为真。由于这两者完全(且默默地)不兼容,并且没有一个明显更正确或更常见,因此 NumPy 拒绝猜测,并要求您明确使用.any().all()

&and |not顺便说一下,和 )可以完全覆盖,因为它们不会短路。覆盖后它们可以返回任何值,NumPy 充分利用了这一点来执行元素级运算,就像它们对其他标量运算所做的那样。另一方面,列表不会在其元素之间广播运算。就像mylist1 - mylist2不代表任何意思 和代表完全不同的东西一样,列表mylist1 + mylist2没有运算符。&

解决方案 4:

示例 1:

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

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

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

示例 2:

正如您所提到的,该&运算符用于按位与。按位运算仅适用于数字。a & b的结果是由ab中均为 1 的位中的 1 组成的数字。例如:

>>> 3 & 1
1

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

>>> 0b0011 & 0b0001
0b0001

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

因此,给出一些关于我的车的陈述

  1. 我的车是红色的

  2. 我的车有轮子

这两个语句的逻辑“与”是:

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

至少对于我的车来说,这两个说法都是正确的。因此,从逻辑上讲,该陈述的价值是真实的。

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

(语句‘我的车是红色的’的数值)&(语句‘我的车有轮子’的数值)=> 数字

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

示例 3 和 4:

Numpy实现数组的算术运算:

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

但没有实现数组的逻辑运算,因为你不能在 python 中重载逻辑运算符。这就是为什么示例三不起作用,而示例四起作用的原因。

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

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

然而,逻辑运算(and,,)却一直被使用。or`not`

解决方案 5:

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

True and 20 
>>> 20

False and 20
>>> False

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

16 & 31
>>> 16
  1. NumPy 不是通灵者,它不知道在逻辑表达式中你的意思是 eg[False, False]应该等于。在这方面,它覆盖了标准的 Python 行为,即:“任何带有is 的True空集合”。len(collection) == 0`False`

  2. 这可能是 NumPy 数组的 & 运算符的预期行为。

解决方案 6:

对于第一个例子,基于django 的文档,

它将始终返回第二个列表,实际上,非空列表被视为 Python 的 True 值,因此 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]

解决方案 7:

使用 Python 列表的操作对列表进行操作list1 and list2检查是否list1为空,list1如果为空则返回,list2如果不是,list1 + list2则将附加list2list1,这样您将获得一个包含元素的新列表len(list1) + len(list2)

仅按元素应用时才有意义的运算符,例如&,引发TypeError,因为如果不循环遍历元素,则不支持按元素操作。

Numpy 数组支持逐元素运算。将计算和中array1 & array2每个对应元素的按位或。将计算和中每个对应元素的总和。array1`array2array1 + array2array1`array2

and这对和不起作用or

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

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

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

解决方案 8:

好问题。与您对示例 1 和 4(或者我应该说 1 & 4 :))的观察类似,我对运算符也有所体验and。numpy和py 的行为也不同。例如:&`sumsumsum`

假设“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])
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用