python - usar - valores booleanos y operaciones logicas
¿Por qué la expresión 0<0== 0 devuelve False en Python? (9)
Aquí está, en todo su esplendor.
>>> class showme(object):
... def __init__(self, name, value):
... self.name, self.value = name, value
... def __repr__(self):
... return "<showme %s:%s>" % (self.name, self.value)
... def __cmp__(self, other):
... print "cmp(%r, %r)" % (self, other)
... if type(other) == showme:
... return cmp(self.value, other.value)
... else:
... return cmp(self.value, other)
...
>>> showme(1,0) < showme(2,0) == showme(3,0)
cmp(<showme 1:0>, <showme 2:0>)
False
>>> (showme(1,0) < showme(2,0)) == showme(3,0)
cmp(<showme 1:0>, <showme 2:0>)
cmp(<showme 3:0>, False)
True
>>> showme(1,0) < (showme(2,0) == showme(3,0))
cmp(<showme 2:0>, <showme 3:0>)
cmp(<showme 1:0>, True)
True
>>>
Mirando en Queue.py en Python 2.6, encontré este constructo que encontré un poco extraño:
def full(self):
"""Return True if the queue is full, False otherwise
(not reliable!)."""
self.mutex.acquire()
n = 0 < self.maxsize == self._qsize()
self.mutex.release()
return n
Si maxsize
es 0, la cola nunca está llena.
Mi pregunta es ¿cómo funciona para este caso? ¿Cómo se considera 0 < 0 == 0
falso?
>>> 0 < 0 == 0
False
>>> (0) < (0 == 0)
True
>>> (0 < 0) == 0
True
>>> 0 < (0 == 0)
True
Como otros mencionaron x comparison_operator y comparison_operator z
es azúcar sintáctica para (x comparison_operator y) and (y comparison_operator z)
con la bonificación de que y solo se evalúa una vez.
Entonces su expresión 0 < 0 == 0
es realmente (0 < 0) and (0 == 0)
, que evalúa False and True
que es False
.
Creo que Python tiene manejo especial de casos para secuencias de operadores relacionales para hacer que las comparaciones de rango sean fáciles de expresar. Es mucho más agradable poder decir 0 < x <= 5
que decir (0 < x) and (x <= 5)
.
Estas se llaman comparaciones encadenadas . Y ese es un enlace a la documentación para ellos.
Con los otros casos de los que habla, el paréntesis obliga a un operador relacional a aplicarse antes que al otro, por lo que ya no son comparaciones encadenadas. Y como True
y False
tienen valores como enteros, obtienes las respuestas que haces de las versiones entre paréntesis.
El extraño comportamiento que experimentas proviene de la habilidad de las pitones para encadenar las condiciones. Como encuentra que 0 no es menor que 0, decide que la expresión completa se evalúa como falsa. Tan pronto como divide esto en condiciones separadas, está cambiando la funcionalidad. Inicialmente, esencialmente está probando que a < b && b == c
para su enunciado original de a < b == c
.
Otro ejemplo:
>>> 1 < 5 < 3
False
>>> (1 < 5) < 3
True
Estoy pensando que Python lo está haciendo entre la magia. Igual que 1 < 2 < 3
significa que 2 está entre 1 y 3.
En este caso, creo que está haciendo que [middle 0] sea mayor que [left 0] e igual a [right 0]. El medio 0 no es mayor que el 0 restante, por lo que se evalúa como falso.
Mirando el desmontaje (los códigos de bytes) es obvio por qué 0 < 0 == 0
es False
.
Aquí hay un análisis de esta expresión:
>>>import dis
>>>def f():
... 0 < 0 == 0
>>>dis.dis(f)
2 0 LOAD_CONST 1 (0)
3 LOAD_CONST 1 (0)
6 DUP_TOP
7 ROT_THREE
8 COMPARE_OP 0 (<)
11 JUMP_IF_FALSE_OR_POP 23
14 LOAD_CONST 1 (0)
17 COMPARE_OP 2 (==)
20 JUMP_FORWARD 2 (to 25)
>> 23 ROT_TWO
24 POP_TOP
>> 25 POP_TOP
26 LOAD_CONST 0 (None)
29 RETURN_VALUE
Observe las líneas 0-8: estas líneas comprueban si 0 < 0
que obviamente devuelve False
en la pila de python.
Ahora observe la línea 11: JUMP_IF_FALSE_OR_POP 23
Esto significa que si 0 < 0
devuelve False
realice un salto a la línea 23.
Ahora, 0 < 0
es False
, por lo que se toma el salto, que deja la pila con un valor False
que es el valor de retorno para toda la expresión 0 < 0 == 0
, aunque la parte == 0
ni siquiera está marcada.
Entonces, para concluir, la respuesta es como se dijo en otras respuestas a esta pregunta. 0 < 0 == 0
tiene un significado especial. El compilador evalúa esto en dos términos: 0 < 0
y 0 == 0
. Al igual que con cualquier expresión booleana compleja con and
entre ellos, si la primera falla, entonces la segunda ni siquiera está marcada.
Espera que esto aclare un poco las cosas, y realmente espero que el método que utilicé para analizar este comportamiento inesperado aliente a otros a intentar lo mismo en el futuro.
Porque
(0 < 0) and (0 == 0)
es False
Puede encadenar operadores de comparación y se expanden automáticamente en las comparaciones por pares.
EDITAR - aclaración sobre True y False en Python
En Python True
y False
son solo instancias de bool
, que es una subclase de int
. En otras palabras, True
realmente es solo 1.
El objetivo de esto es que puedes usar el resultado de una comparación booleana exactamente como un entero. Esto lleva a cosas confusas como
>>> (1==1)+(1==1)
2
>>> (2<1)<1
True
Pero esto solo ocurrirá si entrelazan las comparaciones para que se evalúen primero. De lo contrario, Python expandirá los operadores de comparación.
tal vez este extracto de los docs puede ayudar:
Estos son los llamados métodos de "comparación enriquecida", y se llaman para operadores de comparación con preferencia a
__cmp__()
continuación. La correspondencia entre los símbolos del operador y los nombres de los métodos es la siguiente:x<y
llamadasx.__lt__(y)
,x<=y
llamadasx.__le__(y)
,x==y
llamadasx.__eq__(y)
,x!=y
yx<>y
llamadax.__ne__(y)
,x>y
llamax.__gt__(y)
,x>=y
llamax.__ge__(y)
.Un método de comparación rico puede devolver el singleton
NotImplemented
si no implementa la operación para un par de argumentos dado. Por convención,False
yTrue
se devuelven para una comparación exitosa. Sin embargo, estos métodos pueden devolver cualquier valor, por lo que si el operador de comparación se usa en un contexto booleano (por ejemplo, en la condición de una instrucción if), Python llamará abool()
sobre el valor para determinar si el resultado es verdadero o falso .No hay relaciones implícitas entre los operadores de comparación. La verdad de
x==y
no implica quex!=y
sea falso. En consecuencia, al definir__eq__()
, también se debe definir__ne__()
para que los operadores se comporten como se espera. Consulte el párrafo en__hash__()
para obtener algunas notas importantes sobre la creación de objetos con capacidad de manipulación que admiten operaciones de comparación personalizadas y que se pueden usar como claves del diccionario.No hay versiones de argumentos intercambiados de estos métodos (para usar cuando el argumento de la izquierda no admite la operación pero sí el argumento de la derecha); más bien,
__lt__()
y__gt__()
son reflejo el uno del otro,__le__()
y__ge__()
son reflejo el uno del otro, y__eq__()
y__ne__()
son su propio reflejo.Los argumentos a los métodos de comparación ricos nunca se fuerzan.
Estas fueron comparaciones, pero ya que está encadenando comparaciones , debe saber que:
Las comparaciones se pueden encadenar arbitrariamente, por ejemplo,
x < y <= z
es equivalente ax < y and y <= z
, excepto que y se evalúa solo una vez (pero en ambos casos z no se evalúa cuando x <y se encuentra ser falso).Formalmente, si a, b, c, ..., y, z son expresiones y op1, op2, ..., opN son operadores de comparación, entonces a op1 b op2 c ... y opN z es equivalente a op1 b y b op2 c y ... y opN z, excepto que cada expresión se evalúa como máximo una vez.
>>> 0 < 0 == 0
False
Esta es una comparación encadenada. Devuelve verdadero si cada comparación pairwise es verdadera a su vez. Es el equivalente a (0 < 0) and (0 == 0)
>>> (0) < (0 == 0)
True
Esto es equivalente a 0 < True
que se evalúa como Verdadero.
>>> (0 < 0) == 0
True
Esto es equivalente a False == 0
que se evalúa como True.
>>> 0 < (0 == 0)
True
Equivalente a 0 < True
que, como se indica arriba, se evalúa como Verdadero.