ágil Por que a expressão 0<0== 0 retorna False no Python?




usando django (8)

Olhando para a desmontagem (os códigos de bytes) é óbvio porque 0 < 0 == 0 é False .

Aqui está uma análise desta expressão:

>>>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 as linhas 0-8: Estas linhas verificam se 0 < 0 que obviamente retorna False na pilha de python.

Agora observe a linha 11: JUMP_IF_FALSE_OR_POP 23 Isso significa que se 0 < 0 retornar False execute um salto para a linha 23.

Agora, 0 < 0 é False , então o salto é dado, o que deixa a pilha com um False que é o valor de retorno para toda a expressão 0 < 0 == 0 , mesmo que a parte == 0 não seja verificada.

Então, para concluir, a resposta é como dito em outras respostas a essa pergunta. 0 < 0 == 0 tem um significado especial. O compilador avalia isso para dois termos: 0 < 0 e 0 == 0 . Como acontece com quaisquer expressões booleanas complexas com and entre elas, se a primeira falhar, a segunda não será verificada.

Espero que isso ilumine as coisas um pouco, e eu realmente espero que o método que eu usei para analisar esse comportamento inesperado encoraje os outros a tentar o mesmo no futuro.

Olhando para o Queue.py no Python 2.6, encontrei este constructo que achei um pouco estranho:

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

Se maxsize for 0, a fila nunca estará cheia.

Minha pergunta é como isso funciona para este caso? Como 0 < 0 == 0 é considerado falso?

>>> 0 < 0 == 0
False
>>> (0) < (0 == 0)
True
>>> (0 < 0) == 0
True
>>> 0 < (0 == 0)
True

talvez este trecho da docs possa ajudar:

Estes são os chamados métodos de “comparação rica” e são chamados para operadores de comparação em preferência a __cmp__() abaixo. A correspondência entre os símbolos do operador e os nomes dos métodos é a seguinte: x<y chama x.__lt__(y) , x<=y chama x.__le__(y) , x==y chama x.__eq__(y) , x!=y e x<>y chame x.__ne__(y) , x>y chama x.__gt__(y) , e x>=y chama x.__ge__(y) .

Um método de comparação rico pode retornar o singleton NotImplemented se ele não implementar a operação para um determinado par de argumentos. Por convenção, False e True são retornados para uma comparação bem-sucedida. No entanto, esses métodos podem retornar qualquer valor, portanto, se o operador de comparação for usado em um contexto booleano (por exemplo, na condição de uma instrução if), o Python chamará bool() no valor para determinar se o resultado é verdadeiro ou falso .

Não há relacionamentos implícitos entre os operadores de comparação. A verdade de x==y não implica que x!=y seja falso. Assim, ao definir __eq__() , deve-se também definir __ne__() para que os operadores se comportem como esperado. Veja o parágrafo sobre __hash__() para algumas observações importantes sobre a criação de objetos hashable que suportam operações de comparação customizadas e são utilizáveis ​​como chaves de dicionário.

Não há versões de argumentos trocados desses métodos (para ser usado quando o argumento da esquerda não suporta a operação, mas o argumento correto); em vez disso, __lt__() e __gt__() são reflexos um do outro, __le__() e __ge__() são reflexos um do outro, e __eq__() e __ne__() são seus próprios reflexos.

Argumentos para métodos de comparação ricos nunca são coagidos.

Estas foram comparações, mas como você está encadeando comparações, você deve saber que:

As comparações podem ser encadeadas arbitrariamente, por exemplo, x < y <= z é equivalente a x < y and y <= z , exceto que y é avaliado apenas uma vez (mas em ambos os casos z não é avaliado quando x <y é encontrado ser falso).

Formalmente, se a, b, c, ..., y, z são expressões e op1, op2, ..., opN são operadores de comparação, então op1 b op2 c ... y opNz é equivalente a op1 b e b op2 c e ... y opN z, exceto pelo fato de que cada expressão é avaliada no máximo uma vez.


Como os outros mencionaram x comparison_operator y comparison_operator z é açúcar sintático para (x comparison_operator y) and (y comparison_operator z) com o bônus de que y é avaliado apenas uma vez.

Portanto, sua expressão 0 < 0 == 0 é realmente (0 < 0) and (0 == 0) , que é avaliada como False and True que é apenas False .


>>> 0 < 0 == 0
False

Esta é uma comparação encadeada. Ele retorna true se cada comparação emparelhada for verdadeira. É o equivalente a (0 < 0) and (0 == 0)

>>> (0) < (0 == 0)
True

Isso é equivalente a 0 < True que é avaliado como True.

>>> (0 < 0) == 0
True

Isso é equivalente a False == 0 que é avaliado como True.

>>> 0 < (0 == 0)
True

Equivalente a 0 < True , que, como acima, é avaliado como True.


Aqui está em toda a sua glória.

>>> 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
>>> 

O comportamento estranho que você experimenta vem da capacidade dos pitões de encadear as condições. Como ele encontra 0 não é menor que 0, ele decide que a expressão inteira é avaliada como falsa. Assim que você separar isso em condições separadas, você está mudando a funcionalidade. Inicialmente, ele é essencialmente testando a < b && b == c para sua declaração original de a < b == c .

Outro exemplo:

>>> 1 < 5 < 3
False

>>> (1 < 5) < 3
True

Acredito que o Python tenha tratamento especial de casos para sequências de operadores relacionais para facilitar a comparação de intervalos. É muito melhor poder dizer 0 < x <= 5 do que dizer (0 < x) and (x <= 5) .

Estes são chamados de comparações encadeadas . E isso é um link para a documentação para eles.

Com os outros casos sobre os quais você fala, os parênteses forçam um operador relacional a ser aplicado antes do outro, e assim eles não são mais comparações encadeadas. E como True e False têm valores como inteiros, você obtém as respostas que você faz das versões entre parênteses.


Porque

(0 < 0) and (0 == 0)

é False . Você pode encadear operadores de comparação e eles são automaticamente expandidos nas comparações entre pares.

EDIT - esclarecimento sobre True e False em Python

Em Python, True e False são apenas instâncias de bool , que é uma subclasse de int . Em outras palavras, True é apenas 1.

O ponto disto é que você pode usar o resultado de uma comparação booleana exatamente como um inteiro. Isso leva a coisas confusas como

>>> (1==1)+(1==1)
2
>>> (2<1)<1
True

Mas isso só acontecerá se você colocar em parênteses as comparações de modo que elas sejam avaliadas primeiro. Caso contrário, o Python expandirá os operadores de comparação.







python