Tại sao biểu thức 0 <0 == 0 trả về Sai trong Python?


136

Nhìn vào Queue.py trong Python 2.6, tôi thấy cấu trúc này mà tôi thấy hơi lạ:

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

Nếu maxsizelà 0 thì hàng đợi không bao giờ đầy.

Câu hỏi của tôi là làm thế nào nó hoạt động cho trường hợp này? Làm thế nào 0 < 0 == 0được coi là sai?

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

Là 0 <Đúng bằng với Sai trong python?
Marino Šimić

3
@Marino imić: Từ ví dụ thứ hai được hiển thị trong câu hỏi của OP >>> (0) < (0 == 0), rõ ràng là không.
martineau

3
Một lý do bạn không nên viết mã như n = 0 < self.maxsize == self._qsize()ở nơi đầu tiên, bằng bất kỳ ngôn ngữ nào. Nếu đôi mắt của bạn phải đảo qua lại nhiều lần để tìm hiểu chuyện gì đang xảy ra, thì đó không phải là một dòng được viết tốt. Chỉ cần chia nó thành nhiều dòng.
BlueRaja - Daniel Pflughoeft

2
@Blue: Tôi đồng ý với việc không viết một so sánh như vậy nhưng việc chia nó thành các dòng riêng biệt sẽ hơi quá mức cho hai so sánh. Tôi hy vọng bạn có nghĩa là, chia nó thành các so sánh riêng biệt. ;)
Jeff Mercado

2
@Blue: Tôi không viết nó, nó ở trong Python 2.6. Tôi chỉ cố gắng để hiểu những gì đang xảy ra.
Marcelo Santos

Câu trả lời:


113

Tôi tin rằng Python có xử lý trường hợp đặc biệt cho chuỗi các toán tử quan hệ để làm cho các phép so sánh phạm vi dễ diễn đạt. Có thể nói 0 < x <= 5tốt hơn nhiều so với nói (0 < x) and (x <= 5).

Chúng được gọi là so sánh chuỗi . Và đó là một liên kết đến các tài liệu cho họ.

Với các trường hợp khác mà bạn nói đến, dấu ngoặc đơn buộc một toán tử quan hệ được áp dụng trước toán tử kia, và do đó chúng không còn là chuỗi so sánh. Và vì TrueFalsecó các giá trị là số nguyên, bạn nhận được câu trả lời bạn thực hiện trong các phiên bản được ngoặc đơn.


thật thú vị khi thử một số so sánh này và chỉ định int () và bool (). Tôi nhận ra rằng bool () của bất kỳ khác không là 1. Đoán tôi thậm chí sẽ không bao giờ cố gắng chỉ định trực tiếp bất cứ điều gì ngoài bool (0) hoặc bool (1) trước thí nghiệm suy nghĩ này
j_syk

Bạn có ý định liên kết đến phần này thay thế? docs.python.org/2/reference/expressions.html#comparisons
tavnab

@tavnab - Vâng. Tôi sẽ cố gắng nhớ để sửa nó. Tôi cũng sẽ kiểm tra lịch sử chỉnh sửa. Điều đó dường như không phải là một sai lầm mà tôi sẽ làm. 😕
có nhiều thứ

42

Bởi vì

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

False. Bạn có thể xâu chuỗi các toán tử so sánh với nhau và chúng được tự động mở rộng thành các phép so sánh theo cặp.


EDIT - làm rõ về Đúng và Sai trong Python

Trong Python TrueFalsechỉ là các thể hiện của bool, đó là một lớp con của int. Nói cách khác, Truethực sự chỉ là 1.

Điểm của điều này là bạn có thể sử dụng kết quả so sánh boolean chính xác như một số nguyên. Điều này dẫn đến những điều khó hiểu như

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

Nhưng những điều này sẽ chỉ xảy ra nếu bạn ngoặc đơn so sánh để chúng được đánh giá đầu tiên. Nếu không, Python sẽ mở rộng ra các toán tử so sánh.


2
Tôi đã thấy một cách sử dụng thú vị cho các giá trị boolean đang được sử dụng làm số nguyên ngày hôm qua. Biểu thức 'success' if result_code == 0 else 'failure'có thể được viết lại như ('error', 'success')[result_code == 0]trước đây, tôi chưa bao giờ thấy một boolean được sử dụng để chọn một mục trong danh sách / tuple.
Andrew Clark

'bool' đôi khi được thêm vào khoảng Python 2.2.
MRAB

18

Các hành vi kỳ lạ mà bạn trải nghiệm đến từ khả năng của pythons đối với các điều kiện chuỗi. Vì nó tìm thấy 0 không nhỏ hơn 0, nên nó quyết định toàn bộ biểu thức ước lượng thành false. Ngay khi bạn tách điều này thành các điều kiện riêng biệt, bạn sẽ thay đổi chức năng. Nó ban đầu về cơ bản là thử nghiệm a < b && b == ccho tuyên bố ban đầu của bạn a < b == c.

Một vi dụ khac:

>>> 1 < 5 < 3
False

>>> (1 < 5) < 3
True

1
OMG, a < b && b == cgiống như a < b == cOO
Kiril Kirov

9
>>> 0 < 0 == 0
False

Đây là một so sánh xích. Nó trả về true nếu lần lượt từng cặp so sánh là đúng. Nó tương đương với(0 < 0) and (0 == 0)

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

Điều này tương đương với việc 0 < Trueđánh giá là Đúng.

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

Điều này tương đương với việc False == 0đánh giá là Đúng.

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

Tương đương với 0 < True, như trên, đánh giá là Đúng.


7

Nhìn vào sự phân tách (mã byte), rõ ràng 0 < 0 == 0là tại sao False.

Dưới đây là một phân tích về biểu thức này:

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

Lưu ý các dòng 0-8: Các dòng này kiểm tra xem 0 < 0cái nào rõ ràng sẽ quay trở lại Falsevới ngăn xếp python.

Bây giờ chú ý dòng 11: JUMP_IF_FALSE_OR_POP 23 Điều này có nghĩa là nếu 0 < 0trả về Falsethực hiện một bước nhảy đến dòng 23.

Bây giờ, 0 < 0False, do đó bước nhảy được thực hiện, để lại ngăn xếp với Falsegiá trị trả về cho toàn bộ biểu thức 0 < 0 == 0, mặc dù == 0phần đó thậm chí không được kiểm tra.

Vì vậy, để kết luận, câu trả lời giống như đã nói trong các câu trả lời khác cho câu hỏi này. 0 < 0 == 0có một ý nghĩa đặc biệt. Trình biên dịch đánh giá điều này theo hai thuật ngữ: 0 < 00 == 0. Như với bất kỳ biểu thức boolean phức tạp nào andgiữa chúng, nếu lần đầu tiên thất bại thì lần thứ hai thậm chí không được kiểm tra.

Hy vọng điều này sẽ làm sáng tỏ mọi thứ lên một chút và tôi thực sự hy vọng rằng phương pháp tôi sử dụng để phân tích hành vi bất ngờ này sẽ khuyến khích những người khác thử điều tương tự trong tương lai.


Sẽ không dễ dàng hơn để làm việc đó từ thông số kỹ thuật thay vì kỹ thuật đảo ngược một cách thực hiện cụ thể?
David Heffernan

Không. Đó là câu trả lời ngắn. Tôi tin rằng nó phụ thuộc vào tính cách của bạn. Nếu bạn liên quan đến chế độ xem "hộp đen" và thích nhận câu trả lời của bạn từ thông số kỹ thuật và tài liệu thì câu trả lời này sẽ chỉ khiến bạn bối rối. Nếu bạn thích tìm hiểu và tiết lộ nội bộ của công cụ, thì câu trả lời này là dành cho bạn. Nhận xét của bạn rằng kỹ thuật đảo ngược này chỉ liên quan đến một triển khai cụ thể là chính xác và phải được chỉ ra, nhưng đó không phải là điểm của câu trả lời này. Đây là một minh chứng về việc con trăn dễ dàng lướt qua "dưới mui xe" như thế nào đối với những người đủ tò mò.
SatA

1
Vấn đề với kỹ thuật đảo ngược là thiếu sức mạnh dự đoán. Đó không phải là cách để học một ngôn ngữ mới.
David Heffernan

Một điều nữa mà tôi phải thêm vào, đó là thông số kỹ thuật và tài liệu không phải lúc nào cũng đầy đủ và trong hầu hết các trường hợp sẽ không cung cấp câu trả lời cho các trường hợp cụ thể như vậy. Sau đó, tôi tin rằng, người ta không được sợ khám phá, điều tra và tiếp cận sâu hơn khi cần thiết để có câu trả lời.
SatA

2

Như những người khác đã đề cập x comparison_operator y comparison_operator zlà đường cú pháp (x comparison_operator y) and (y comparison_operator z)với phần thưởng mà y chỉ được đánh giá một lần.

Vì vậy, biểu hiện của bạn 0 < 0 == 0là thực sự (0 < 0) and (0 == 0), mà đánh giá False and Truelà chỉ False.


2

có lẽ đoạn trích này từ các tài liệu có thể giúp đỡ:

Đây là những phương pháp được gọi là phương pháp so sánh phong phú của người dùng và được gọi cho các toán tử so sánh theo sở thích __cmp__()bên dưới. Sự tương ứng giữa các ký hiệu toán tử và tên phương pháp như sau: x<ycuộc gọi x.__lt__(y), x<=ycuộc gọi x.__le__(y), x==ycuộc gọi x.__eq__(y), x!=yx<>y cuộc gọi x.__ne__(y), x>ycuộc gọi x.__gt__(y)x>=ycuộc gọi x.__ge__(y).

Một phương thức so sánh phong phú có thể trả về singleton NotImplementednếu nó không thực hiện thao tác cho một cặp đối số đã cho. Theo quy ước, FalseTrueđược trả lại để so sánh thành công. Tuy nhiên, các phương thức này có thể trả về bất kỳ giá trị nào, vì vậy nếu toán tử so sánh được sử dụng trong ngữ cảnh Boolean (ví dụ: trong điều kiện của câu lệnh if), Python sẽ gọi bool()giá trị đó để xác định xem kết quả là đúng hay sai.

Không có mối quan hệ ngụ ý giữa các nhà khai thác so sánh. Sự thật x==ykhông ngụ ý đó x!=y là sai. Theo đó, khi xác định __eq__(), người ta cũng nên xác định __ne__()để các toán tử sẽ hành xử như mong đợi. Xem đoạn trên __hash__()để biết một số lưu ý quan trọng về việc tạo các đối tượng có thể băm có hỗ trợ các hoạt động so sánh tùy chỉnh và có thể sử dụng làm khóa từ điển.

Không có phiên bản đối số hoán đổi của các phương thức này (được sử dụng khi đối số bên trái không hỗ trợ thao tác nhưng đối số bên phải thì không); đúng hơn, __lt__()__gt__() là sự phản ánh của nhau, __le__()__ge__()là sự phản ánh của nhau, __eq__()__ne__() là sự phản ánh của chính họ.

Đối số với các phương pháp so sánh phong phú không bao giờ bị ép buộc.

Đây là những so sánh nhưng vì bạn đang so sánh các chuỗi so sánh nên bạn biết rằng:

Việc so sánh có thể được xâu chuỗi tùy ý, ví dụ, x < y <= ztương đương với x < y and y <= z, ngoại trừ việc y chỉ được đánh giá một lần (nhưng trong cả hai trường hợp z đều không được đánh giá khi x <y bị sai).

Chính thức, nếu a, b, c, ..., y, z là biểu thức và op1, op2, ..., opN là toán tử so sánh, thì op1 b op2 c ... y opN z tương đương với op1 b và b op2 c và ... y opN z, ngoại trừ mỗi biểu thức được ước tính nhiều nhất một lần.


1

Nó ở đây trong tất cả ánh hào quang của nó.

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

0

Tôi đang nghĩ Python đang làm điều đó thật kỳ lạ giữa ma thuật. Tương tự như 1 < 2 < 3phương tiện 2 nằm trong khoảng từ 1 đến 3.

Trong trường hợp này, tôi nghĩ rằng nó đang làm [giữa 0] lớn hơn [trái 0] và bằng [phải 0]. Trung 0 không lớn hơn trái 0, do đó, nó đánh giá là sai.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.