Python! = Thao tác vs không phải là hung


250

Trong một bình luận về câu hỏi này , tôi đã thấy một tuyên bố được khuyến nghị sử dụng

result is not None

đấu với

result != None

Tôi đã tự hỏi sự khác biệt là gì, và tại sao người ta có thể được đề nghị hơn người khác?



1
Hừm. Mặc dù câu trả lời cho cả hai câu hỏi là cùng một khái niệm, tôi nghĩ rằng các câu trả lời và câu trả lời chi tiết ở đây có đóng góp độc lập cho khái niệm kiểm tra danh tính và bình đẳng.
viksit

Câu trả lời:


301

==là một bài kiểm tra bình đẳng . Nó kiểm tra xem phía bên tay phải và phía bên trái có phải là các đối tượng bằng nhau hay không (theo phương pháp __eq__hoặc __cmp__phương pháp của họ .)

islà một bài kiểm tra danh tính . Nó kiểm tra xem phía bên tay phải và phía bên trái có phải là cùng một đối tượng hay không. Không có phương thức nào được thực hiện, các đối tượng không thể ảnh hưởng đếnis hoạt động.

Bạn sử dụng is(và is not) cho các singletons, như None, nơi bạn không quan tâm đến các đối tượng có thể muốn giả vờ Nonehoặc nơi bạn muốn bảo vệ chống lại các vật thể bị phá vỡ khi so sánh với None.


3
Cảm ơn câu trả lời - bạn có thể giải thích các tình huống khi một vật thể có thể vỡ, được so sánh với Không?
viksit

3
@viksit. Nonecó ít phương thức và hầu như không có thuộc tính. Nếu __eq__thử nghiệm của bạn mong đợi một phương thức hoặc thuộc tính, nó có thể bị hỏng. def __eq__( self, other ): return self.size == other.size. Ví dụ, sẽ phá vỡ nếu otherxảy ra None.
S.Lott

36
Cách ưa thích của tôi để hiểu điều này là: Python isgiống như Java ==. Python ==giống như của Java .equals(). Tất nhiên điều này chỉ có ích nếu bạn biết Java.
MatrixFrog

4
@MatrixFrog: Trong PHP hoặc JavaScript, chúng tôi sẽ nói rằng isnó giống như ===(rất bằng nhau) và ngược lại is notlà như thế !==(không chính xác bằng nhau).
Orwellophile

3
is notmột nhà điều hành duy nhất hoặc nó chỉ phủ nhận kết quả của isnội bộ như thế not foo is barnào?
Asad Moosvi

150

Đầu tiên, hãy để tôi đi qua một vài điều khoản. Nếu bạn chỉ muốn câu hỏi của mình được trả lời, hãy cuộn xuống "Trả lời câu hỏi của bạn".

Định nghĩa

Nhận dạng đối tượng : Khi bạn tạo một đối tượng, bạn có thể gán nó cho một biến. Sau đó bạn cũng có thể gán nó cho một biến khác. Và một cái khác.

>>> button = Button()
>>> cancel = button
>>> close = button
>>> dismiss = button
>>> print(cancel is close)
True

Trong trường hợp này, cancel, close, và dismisstất cả các tham chiếu đến cùng một đối tượng trong bộ nhớ. Bạn chỉ tạo một Buttonđối tượng và cả ba biến tham chiếu đến một đối tượng này. Chúng tôi nói rằng cancel, closedismisstất cả đều đề cập đến các đối tượng giống hệt nhau ; nghĩa là, họ đề cập đến một đối tượng duy nhất.

Bình đẳng đối tượng : Khi bạn so sánh hai đối tượng, bạn thường không quan tâm mà nó đề cập đến chính xác cùng một đối tượng trong bộ nhớ. Với sự bình đẳng đối tượng, bạn có thể xác định quy tắc của riêng mình để so sánh hai đối tượng. Khi bạn viết if a == b:, về cơ bản bạn đang nói if a.__eq__(b):. Điều này cho phép bạn xác định một __eq__phương thức trên ađể bạn có thể sử dụng logic so sánh của riêng bạn.

Cơ sở lý luận để so sánh bình đẳng

Đặt vấn đề: Hai đối tượng có cùng một dữ liệu, nhưng không giống nhau. (Chúng không phải là cùng một đối tượng trong bộ nhớ.) Ví dụ: String

>>> greeting = "It's a beautiful day in the neighbourhood."
>>> a = unicode(greeting)
>>> b = unicode(greeting)
>>> a is b
False
>>> a == b
True

Lưu ý: Tôi sử dụng các chuỗi unicode ở đây vì Python đủ thông minh để sử dụng lại các chuỗi thông thường mà không tạo các chuỗi mới trong bộ nhớ.

Ở đây, tôi có hai chuỗi unicode, ab. Chúng có cùng một nội dung, nhưng chúng không phải là cùng một đối tượng trong bộ nhớ. Tuy nhiên, khi chúng ta so sánh chúng, chúng ta muốn chúng so sánh bằng nhau. Điều đang xảy ra ở đây là đối tượng unicode đã thực hiện __eq__phương thức này.

class unicode(object):
    # ...

    def __eq__(self, other):
        if len(self) != len(other):
            return False

        for i, j in zip(self, other):
            if i != j:
                return False

        return True

Lưu ý: __eq__trên unicodechắc chắn được thực hiện hiệu quả hơn thế này.

Đặt vấn đề: Hai đối tượng có dữ liệu khác nhau, nhưng được coi là cùng một đối tượng nếu một số dữ liệu chính là như nhau. Ví dụ: Hầu hết các loại dữ liệu mô hình

>>> import datetime
>>> a = Monitor()
>>> a.make = "Dell"
>>> a.model = "E770s"
>>> a.owner = "Bob Jones"
>>> a.warranty_expiration = datetime.date(2030, 12, 31)
>>> b = Monitor()
>>> b.make = "Dell"
>>> b.model = "E770s"
>>> b.owner = "Sam Johnson"
>>> b.warranty_expiration = datetime.date(2005, 8, 22)
>>> a is b
False
>>> a == b
True

Ở đây, tôi có hai màn hình Dell, ab. Họ có cùng kiểu dáng và mẫu mã. Tuy nhiên, chúng không có cùng dữ liệu và cũng không phải là cùng một đối tượng trong bộ nhớ. Tuy nhiên, khi chúng ta so sánh chúng, chúng ta muốn chúng so sánh bằng nhau. Điều đang xảy ra ở đây là đối tượng Monitor đã thực hiện __eq__phương thức này.

class Monitor(object):
    # ...

    def __eq__(self, other):
        return self.make == other.make and self.model == other.model

Trả lời câu hỏi của bạn

Khi so sánh với None, luôn luôn sử dụngis not . Không có gì là một singleton trong Python - chỉ có một phiên bản của nó trong bộ nhớ.

Bằng cách so sánh danh tính , điều này có thể được thực hiện rất nhanh. Python kiểm tra xem đối tượng bạn đang đề cập có cùng địa chỉ bộ nhớ với đối tượng Không toàn cầu hay không - so sánh hai số rất nhanh.

Bằng cách so sánh sự bằng nhau , Python phải tìm kiếm xem đối tượng của bạn có __eq__phương thức hay không. Nếu không, nó sẽ kiểm tra từng siêu lớp tìm kiếm một __eq__phương thức. Nếu nó tìm thấy một, Python gọi nó. Điều này đặc biệt tệ nếu __eq__phương thức chậm và không quay lại ngay lập tức khi thông báo rằng đối tượng kia làNone .

Bạn đã không thực hiện __eq__? Sau đó, Python có thể sẽ tìm __eq__phương thức trên objectvà sử dụng phương thức đó thay vào đó - chỉ kiểm tra danh tính đối tượng.

Khi so sánh hầu hết những thứ khác trong Python, bạn sẽ sử dụng !=.


42

Hãy xem xét những điều sau đây:

class Bad(object):
    def __eq__(self, other):
        return True

c = Bad()
c is None # False, equivalent to id(c) == id(None)
c == None # True, equivalent to c.__eq__(None)

1
Đây là một ví dụ rất hữu ích và đơn giản. Cảm ơn bạn.
msarafzadeh

18

Nonelà một singleton, do đó so sánh danh tính sẽ luôn hoạt động, trong khi một đối tượng có thể giả mạo so sánh bình đẳng thông qua .__eq__().


Ah thú vị! Trong tình huống nào người ta có thể muốn giả mạo so sánh bình đẳng btw? Tôi đoán điều này có ý nghĩa bảo mật theo một cách nào đó.
viksit

1
Đó không phải là giả mạo bình đẳng, mà là thực hiện bình đẳng. Có rất nhiều lý do để muốn xác định cách một đối tượng so sánh với đối tượng khác.
Thomas Wouters

1
Tôi muốn nói rằng nó có nhiều hàm ý nhầm lẫn hơn là ngụ ý bảo mật.
Greg Hewgill

2
Tôi đã không đưa ra một lý do để chống lại sự bình đẳng giả None, nhưng hành vi không chính xác liên quan Nonecó thể xảy ra như là một tác dụng phụ của việc thực hiện bình đẳng chống lại các loại khác. Đó không phải là quá nhiều ý nghĩa bảo mật vì nó chỉ là ý nghĩa chính xác.
Ignacio Vazquez-Abrams

Ah theo cách đó, tôi thấy. Thx để làm rõ.
viksit

10
>>> () là ()
Thật
>>> 1 là 1
Thật
>>> (1,) == (1,)
Thật
>>> (1,) là (1,)
Sai
>>> a = (1,)
>>> b = a
>>> a là b
Thật

Một số đối tượng là singletons, và do đó isvới chúng là tương đương với ==. Hầu hết là không.


4
Hầu hết trong số này chỉ hoạt động bởi sự trùng hợp / chi tiết thực hiện. ()1vốn không phải là singletons.
Mike Graham

1
Trong triển khai CPython, các số nguyên nhỏ ( -NSMALLNEGINTS <= n <= NSMALLPOSINTS) và các bộ dữ liệu trống là các singletons. Quả thực nó không được ghi nhận cũng không được bảo đảm, nhưng nó không có khả năng thay đổi.
ephemient

3
Đó là cách nó được thực hiện, nhưng nó không có ý nghĩa hoặc hữu ích hoặc giáo dục.
Mike Graham

1
Và đặc biệt, CPython không phải là triển khai Python duy nhất. Dựa vào hành vi có thể thay đổi trong các triển khai Python dường như thường là một ý tưởng tồi ™ đối với tôi.
me_ và
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.