¿Cómo obtengo el opuesto (negación) de un booleano en Python?

106

Para la siguiente muestra:

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

¿Hay alguna forma de omitir la segunda instrucción if? ¿Solo para decirle a la computadora que devuelva lo opuesto al booleano bool?

amiasina
fuente
6
Esto es probablemente sólo pseudocódigo, pero inty boolson los dos nombres incorporados (para los tipos que representan), y no se debe utilizar como nombres de variables.
SingleNegationElimination
sí, es solo un pseudocódigo, solo con fines de demostración ...
amyassin
7
if x == True:debe escribirse if x:.
Mike Graham

Respuestas:

180

Puedes usar:

return not bool
jtbandes
fuente
4
Además, int in range (....)es ineficiente. Creará una lista y luego realizará una búsqueda lineal. Mejor de lo que x in range(low, high)es low <= x < high.
MRAB
Tenga en cuenta que not Nonedevolverá False. Al menos para mí eso no es lo que esperaba. Yo esperaría not Noneque ser Noneasí notse puede utilizar para negar incluso si el valor de retorno podría ser None. La forma en que está implementado en Python, primero tendrá que verificar que realmente obtuvo un booleano.
omni
54

El notoperador (negación lógica)

Probablemente la mejor forma sea utilizando el operador not:

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

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

Entonces, en lugar de tu código:

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

Podrías usar:

return not bool

La negación lógica como función

También hay dos funciones en el operatormódulo operator.not_y su alias operator.__not__en caso de que lo necesite como función en lugar de como operador:

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

Estos pueden ser útiles si desea utilizar una función que requiere una función de predicado o una devolución de llamada.

Por ejemplo mapo 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]

Por supuesto, también se podría lograr lo mismo con una lambdafunción equivalente :

>>> my_not_function = lambda item: not item

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

No utilice el operador de inversión bit a bit ~en valores booleanos

Uno podría tener la tentación de usar el operador de inversión bit a bit ~o la función de operador equivalente operator.inv(o uno de los otros 3 alias allí). Pero debido a que booles una subclase del intresultado que podría ser inesperado porque no devuelve el "booleano inverso", devuelve el "entero inverso":

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

Eso es porque Truees equivalente a 1y Falsea 0e inversión a nivel de bits opera en la representación bit a bit de los números enteros 1 y 0.

Por tanto, estos no se pueden utilizar para "negar" a bool.

Negación con matrices (y subclases) NumPy

Si está tratando con matrices NumPy (o subclases como pandas.Serieso pandas.DataFrame) que contienen booleanos, en realidad puede usar el operador inverso bit a bit ( ~) para negar todos los booleanos en una matriz:

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

O la función NumPy equivalente:

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

No puede usar el notoperador o la operator.notfunción en arreglos NumPy porque estos requieren que estos devuelvan un solo bool(no un arreglo de valores booleanos), sin embargo, NumPy también contiene una función no lógica que funciona por elementos:

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

Eso también se puede aplicar a matrices no booleanas:

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

Personalizando tus propias clases

notfunciona invocando boolel valor y niega el resultado. En el caso más simple, el valor de verdad simplemente llamará __bool__al objeto.

Entonces, al implementar __bool__(o __nonzero__en Python 2) puede personalizar el valor de verdad y, por lo tanto, el resultado de 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)

Agregué una printdeclaración para que pueda verificar que realmente llama al método:

>>> a = Test(10)
>>> not a
__bool__ called on Test(10)
False

Del mismo modo, podría implementar el __invert__método para implementar el comportamiento cuando ~se aplica:

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)

Nuevamente con una printllamada para ver que realmente se llama:

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

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

Sin embargo, implementarlo __invert__así podría resultar confuso porque su comportamiento es diferente del comportamiento "normal" de Python. Si alguna vez lo hace, documente claramente y asegúrese de que tenga un caso de uso bastante bueno (y común).

MSeifert
fuente
11

Python tiene un operador "no", ¿verdad? ¿No es simplemente "no"? Como en,

  return not bool
Patrick87
fuente
2

Puedes comparar la matriz booleana. Por ejemplo

X = [True, False, True]

entonces

Y = X == False

te daría

Y = [False, True, False]
Sebastián
fuente
1
Para una matriz Numpy, tal vez, pero para una lista estándar de Python, esto es incorrecto. Dado que el OP no menciona tampoco, no veo cómo esto responde a la pregunta.
SiHa
2

Si está intentando implementar un conmutador , de modo que cada vez que vuelva a ejecutar un código persistente se niegue, puede lograrlo de la siguiente manera:

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

La ejecución de este código establecerá primero el toggleto Truey cada vez que se llame a este fragmento, se anulará el cambio.

usuario1767754
fuente
2

La respuesta aceptada aquí es la más correcta para el escenario dado.

Sin embargo, me hizo preguntarme sobre simplemente invertir un valor booleano en general. Resulta que la solución aceptada aquí funciona como una sola línea, y hay otra que funciona también. Suponiendo que tiene una variable "n" que sabe que es booleana, las formas más fáciles de invertirla son:

n = n is False

cuál fue mi solución original, y luego la respuesta aceptada de esta pregunta:

n = not n

El último ES más claro, pero me pregunté sobre el rendimiento y lo engañé timeit, y resulta que n = not ntambién es la forma MÁS RÁPIDA de invertir el valor booleano.

usuario1411616
fuente
No lo use n is Falsepara probar la verdad.
gerrit
0

Otra forma de lograr el mismo resultado, que encontré útil para un marco de datos de pandas.

Como se sugiere a continuación por mousetail:

bool(1 - False)

bool(1 - True)
Cándido
fuente
¿Por qué no bool(1 - False)?
cola de ratón
Derecha. Eso es mejor y más corto.
Candide
¿No hay una inversión lógica de los marcos de datos de pandas, como con las matrices numpy?
gerrit