Python 两个布尔列表上的 AND 运算符 - 怎么样?

Python AND operator on two boolean lists - how?

我有两个布尔列表,例如

x=[True,True,False,False]
y=[True,False,True,False]

我想将这些列表与预期的输出相结合:

xy=[True,False,False,False]

我认为表达式 x and y 会起作用,但后来发现它不起作用:事实上,(x and y) != (y and x)

x and y 的输出:[True,False,True,False]

y and x 的输出:[True,True,False,False]

使用列表理解 是否有正确的输出。哇!

xy = [x[i] and y[i] for i in range(len(x)]

请注意,我找不到任何参考资料告诉我 AND 运算符可以像我尝试使用 x 和 y 那样工作。但是在 Python 中尝试事情很容易。 有人可以向我解释 x and y 发生了什么吗?

这是一个简单的测试程序:

import random
random.seed()
n = 10
x = [random.random() > 0.5 for i in range(n)]
y = [random.random() > 0.5 for i in range(n)]
# Next two methods look sensible, but do not work
a = x and y
z = y and x
# Next: apparently only the list comprehension method is correct
xy = [x[i] and y[i] for i in range(n)]
print 'x        : %s'%str(x)
print 'y        : %s'%str(y)
print 'x and y  : %s'%str(a)
print 'y and x  : %s'%str(z)
print '[x and y]: %s'%str(xy)

这应该可以满足您的要求:

xy = [a and b for a, b in zip(x, y)]

x and y returns yy and x returns x 的原因是因为 python 中的布尔运算符return 确定表达式真实性的最后一个检查值。非空 list 的计算结果为 True,并且由于 and 需要两个操作数来计算 True,最后检查的操作数是第二个操作数。与 x or y 对比,后者会 return x 因为它不需要检查 y 来确定表达式的真实性。

您可以使用zip函数

x=[True,True,False,False]
y=[True,False,True,False]
z=[a and b for a,b in zip(x,y)]

and 只是 returns 第一个或第二个操作数,基于它们的真值。如果第一个操作数被认为是 false,则返回它,否则返回另一个操作数。

不为空 时,列表被视为 true,因此两个列表都被视为 true。它们的内容在这里不起作用

因为两个列表都不为空,x and y简单地returns第二个列表对象;只有当 x 为空时才会返回:

>>> [True, False] and ['foo', 'bar']
['foo', 'bar']
>>> [] and ['foo', 'bar']
[]

请参阅 Python 文档中的 Truth value testing section

Any object can be tested for truth value, for use in an if or while condition or as operand of the Boolean operations below. The following values are considered false:

[...]

  • any empty sequence, for example, '', (), [].

[...]

All other values are considered true — so objects of many types are always true.

(强调我的),以及正下方的 Boolean operations section

x and y
if x is false, then x, else y

This is a short-circuit operator, so it only evaluates the second argument if the first one is True.

您确实需要明确测试列表中 contained 的值。正如您发现的那样,您可以通过列表理解来做到这一点。您可以使用 zip() function 重写它以配对值:

[a and b for a, b in zip(x, y)]

and 不一定是布尔运算符;它 returns 它的两个参数之一,无论它们的类型如何。如果第一个参数是 false-ish(False、数字零或空 string/container),则它 returns 该参数。否则,它 returns 第二个参数。

在你的例子中,xy 都是非空列表,所以第一个参数总是 true-ish,意思是 x and y returns yy and x returns x.

你可以使用 numpy:

>>> import numpy as np
>>> x=np.array([True,True,False,False])
>>> y=np.array([True,False,True,False])
>>> x & y
array([ True, False, False, False], dtype=bool)

Numpy 允许对数组进行数值和逻辑运算,例如:

>>> z=np.array([1,2,3,4])
>>> z+1
array([2, 3, 4, 5])

您可以使用 & 运算符按位执行和。

您可以像这样使用 numpy 直接生成布尔数组,而不是列表理解:

>>> np.random.random(10)>.5
array([ True,  True,  True, False, False,  True,  True, False, False, False], dtype=bool)

而不是使用

[a and b for a, b in zip(x, y)]

可以利用 numpy 的可能性来乘以布尔值:

(np.array(x)*np.array(y))
>> array([ True, False, False, False], dtype=bool)

还是我忽略了一个特例?

除了@Martijn Pieters 已经回答的内容之外,我只想添加以下代码来解释 andor 操作。

and returns 遇到的第一个虚假值否则是最后评估的参数。

类似地 or returns 遇到第一个真值,否则最后一个评估的参数。

nl1 = [3,3,3,3,0,0,0,0]
nl2 = [2,2,0,0,2,2,0,0]
nl3 = [1,0,1,0,1,0,1,0]
and_list = [a and b and c for a,b,c in zip(nl1,nl2,nl3)]
or_list = [a or b or c for a,b,c in zip(nl1,nl2,nl3)]

值为

and_list = [1, 0, 0, 0, 0, 0, 0, 0]

or_list = [3, 3, 3, 3, 2, 2, 1, 0]

感谢@Martijn Pieters 和@Tony 的回答。 我深入研究了我们必须对两个列表进行 AND 操作的各种选项的时间安排,我想分享我的结果,因为我发现它们很有趣。

尽管很喜欢 pythonic 方式 [a and b for a,b in zip(x,y) ],但结果真的很慢。 我与数组的整数乘积 (1*(bool 数组)) * (1*(bool 数组)) 进行比较,结果速度快了 10 倍以上

import time
import numpy as np
array_to_filter = np.linspace(1,1000000,1000000)                # 1 million of integers :-)
value_limit = 100
cycles = 100

# METHOD #1:  [a and b for a,b in zip(x,y) ]
t0=time.clock()
for jj in range(cycles):
    x = array_to_filter<np.max(array_to_filter)-value_limit   # filter the values > MAX-value_limit
    y = array_to_filter>value_limit                          # filter the values < value_limit
    z= [a and b for a,b in zip(x,y) ]                       # AND
    filtered = array_to_filter[z]
print('METHOD #1 = %.2f s' % ( (time.clock()-t0)))



# METHOD 1*(array of bool) AND  1*(array of bool)
t0=time.clock()
for jj in range(cycles):
    x = 1*(array_to_filter<np.max(array_to_filter)-value_limit)   # filter the values > MAX-value_limit
    y = 1*(array_to_filter>value_limit)                          # filter the values < value_limit
    z = x*y                                                     # AND
    z = z.astype(bool)                                          # convert back to array of bool
    filtered = array_to_filter[z]
print('METHOD #2 = %.2f s' % ( (time.clock()-t0)))

结果是

METHOD #1 = 15.36 s
METHOD #2 = 1.85 s

数组大小或循环次数对速度的影响几乎相同。
我希望我帮助某人编码更快。 :-)

这是一个简单的解决方案:

np.logical_and(x,y)

要概括 zip 方法,对任意数量的列表使用 allany

all 和:

[all(i) for i in zip(a, b, c)]  # zip all lists

any 或:

[any(i) for i in zip(a, b, c)]