如何在Python中获取布尔值的相反值(否定)?

203

对于以下示例:

def fuctionName(int, bool):
    if int in range(...):
        if bool == True:
            return False
        else:
            return True

有没有办法跳过第二个 if 语句?只是告诉计算机返回布尔值 bool 的相反值吗?


10
这很可能只是虚拟代码,但 intbool 都是内置名称(表示它们代表的类型),不应作为变量名使用。我会为你翻译这句话,并尽可能使其通俗易懂,但不会改变原意。 - SingleNegationElimination
1
是的,这只是伪代码,仅用于演示目的... - amyassin
11
如果 x == True:应该写成 if x:。 - Mike Graham
8个回答

320

4
int in range (....)这种写法效率不高,它会创建一个列表,然后进行线性搜索。比起x in range(low, high),更好的写法是low <= x < high - MRAB
请注意,not None 将返回 False。至少对我来说,这不是我所期望的。我希望 not NoneNone,这样即使返回值可能是 None,也可以使用 not 进行否定。在 Python 中实现的方式是,您必须先验证您实际上获得了一个布尔值。 - omni
1
我认为我需要提到@MRAB提到的int in range(...)只在Python 3之前是低效的,因为除非你通过list(...)运行range(...),否则它使用了一种极其高效的方式(<-我的理解)。不过,我还没有检查过那部分的C代码,所以我不知道他们是如何做到的。 - rautamiekka
C 代码在这里,它基本上是一个用于 range 对象的自定义 __contains__:https://github.com/python/cpython/blob/a330365ca5ae836075f306334ab648bf23471481/Objects/rangeobject.c#L430-L431 - jtbandes

107

not 运算符(逻辑非)

最好的方法可能就是使用 not 运算符:

>>> value = True
>>> not value
False

>>> value = False
>>> not value
True

所以,代替你的代码:

if bool == True:
    return False
else:
    return True

您可以使用:
return not bool

逻辑否定作为函数

operator 模块中也有两个函数,分别为 operator.not_ 和它的别名 operator.__not__,如果你需要把它们作为函数使用而不是运算符:

>>> import operator
>>> operator.not_(False)
True
>>> operator.not_(True)
False

如果您想使用需要谓词函数或回调函数的函数,则可以使用它们。

例如:map 或者 filter

>>> lst = [True, False, True, False]
>>> list(map(operator.not_, lst))
[False, True, False, True]

>>> lst = [True, False, True, False]
>>> list(filter(operator.not_, lst))
[False, False]

当然,同样的效果也可以通过等效的lambda函数实现:

>>> my_not_function = lambda item: not item

>>> list(map(my_not_function, lst))
[False, True, False, True]

不要在布尔值上使用位反操作符~

有人可能会尝试使用位反操作符~或等效的操作符函数operator.inv(或其中的其他3个别名)。 但由于boolint的子类,因此结果可能出乎意料,因为它不会返回“逆布尔值”,而会返回“逆整数”:

>>> ~True
-2
>>> ~False
-1

这是因为True等同于1,而False等同于0,按位反转作用于整数10的位表示。

因此,它们不能用于“否定”bool

使用NumPy数组(及其子类)进行取反

如果您正在处理包含布尔值的NumPy数组(或子类,如pandas.Seriespandas.DataFrame),实际上可以使用按位反转运算符(~)来取反数组中所有的布尔值:

>>> import numpy as np
>>> arr = np.array([True, False, True, False])
>>> ~arr
array([False,  True, False,  True])

或等价的 NumPy 函数:

>>> np.bitwise_not(arr)
array([False,  True, False,  True])

你不能在NumPy数组上使用not运算符或operator.not函数,因为这些需要返回单个bool(而不是布尔数组),但是NumPy也包含按元素计算的逻辑非函数:

>>> np.logical_not(arr)
array([False,  True, False,  True])

这也适用于非布尔数组:

>>> arr = np.array([0, 1, 2, 0])
>>> np.logical_not(arr)
array([ True, False, False,  True])

自定义你自己的类

not 会对值调用 bool 并取反结果。在最简单的情况下,真值测试只会调用对象的 __bool__ 方法。

因此,通过实现 __bool__ (或 Python 2 中的 __nonzero__ 方法),您可以自定义真值并从而自定义 not 的结果:

class Test(object):
    def __init__(self, value):
        self._value = value

    def __bool__(self):
        print('__bool__ called on {!r}'.format(self))
        return bool(self._value)

    __nonzero__ = __bool__  # Python 2 compatibility

    def __repr__(self):
        return '{self.__class__.__name__}({self._value!r})'.format(self=self)

我添加了一个print语句,以便您可以验证它是否真的调用了该方法:

我添加了一个print语句,以便您可以验证它是否真的调用了该方法。
>>> a = Test(10)
>>> not a
__bool__ called on Test(10)
False

同样地,您可以实现__invert__方法来实现当应用~时的行为:
class Test(object):
    def __init__(self, value):
        self._value = value

    def __invert__(self):
        print('__invert__ called on {!r}'.format(self))
        return not self._value

    def __repr__(self):
        return '{self.__class__.__name__}({self._value!r})'.format(self=self)

再次使用print调用以检查它是否被调用:

>>> a = Test(True)
>>> ~a
__invert__ called on Test(True)
False

>>> a = Test(False)
>>> ~a
__invert__ called on Test(False)
True

然而,像这样实现 __invert__ 可能会令人困惑,因为它的行为与“正常”的Python行为不同。如果您确实这样做,请清楚地记录下来,并确保它具有相当好(和常见的)用例。


12

Python有一个“not”运算符,对吗?它不仅仅是“not”吗?就像这样:

  return not bool

6

在这里接受的答案是针对给定场景最正确的。

不过,它让我想到了一般情况下简单地翻转布尔值。事实证明,在这里接受的解决方案可以作为一行代码解决,还有另一个一行代码的解决方案也可行。假设您有一个名为“n”的变量,您知道它是布尔类型,反转它最简单的方法是:

n = n is False

这是我最初的解决方案,然后是从这个问题中接受的答案:

n = not n

后者更加清晰,但我对性能有疑问,并使用timeit进行了测试 - 结果表明,在反转布尔值时,n = not n也是更快的方法。


不要使用 n is False 进行真值测试。 - gerrit

2
你可以直接比较布尔数组。例如:
X = [True, False, True]

那么

Y = X == False

将为你提供

Y = [False, True, False]

1
对于一个Numpy数组,也许是这样的,但对于标准的Python列表来说,这是不正确的。由于OP没有提到其中任何一个,我不明白这个回答如何解决问题。 - SiHa

2

我认为最简洁的版本是

self.foobar ^= True

它不需要重复整个名称,并且适用于纯布尔值。


2

如果您想实现一个切换开关,以便每次重新运行持久代码时都被否定,您可以按照以下方式实现:

try:
    toggle = not toggle
except NameError:
    toggle = True

运行此代码将首先将toggle设置为True,每当调用此片段时,toggle将被取反。

1

另一种实现相同结果的方法,我发现对于pandas数据帧非常有用。

如下所建议的mousetail:

bool(1 - False)

bool(1 - True)

Pandas的数据框架没有类似于numpy数组的逻辑反转吗? - gerrit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接