Tại sao việc so sánh các chuỗi bằng cách sử dụng '==' hoặc 'là' đôi khi tạo ra một kết quả khác nhau?


1147

Tôi đã có một chương trình Python trong đó hai biến được đặt thành giá trị 'public'. Trong một biểu thức có điều kiện, tôi có sự so sánh var1 is var2thất bại, nhưng nếu tôi thay đổi nó thành var1 == var2trả về True.

Bây giờ nếu tôi mở trình thông dịch Python của mình và thực hiện so sánh "là" tương tự, nó đã thành công.

>>> s1 = 'public'
>>> s2 = 'public'
>>> s2 is s1
True

Tôi đang thiếu gì ở đây?



3
Vấn đề này cũng xảy ra khi bạn đọc một đầu vào giao diện điều khiển thông qua ví dụ : input = raw_input("Decide (y/n): "). Trong trường hợp này, đầu vào là "y" và if input == 'y':sẽ trả về "Đúng" trong khi if input is 'y':sẽ trả về Sai.
Semjon Mössinger ngày 21/8/2016

4
Blog này cung cấp một lời giải thích đầy đủ hơn nhiều so với bất kỳ câu trả lời nào guilload.com/python-opes-iterning
Chris_Rands

1
Như @ chris-rico đề cập, tôi giải thích rất hay ở đây stackoverflow.com/q/15541404/1695680
ThorSummoner

Câu trả lời:


1533

islà kiểm tra danh tính, ==là kiểm tra bình đẳng. những gì xảy ra trong mã của bạn sẽ được mô phỏng trong trình thông dịch như thế này:

>>> a = 'pub'
>>> b = ''.join(['p', 'u', 'b'])
>>> a == b
True
>>> a is b
False

vì vậy, không có gì ngạc nhiên khi chúng không giống nhau, phải không?

Nói cách khác: isid(a) == id(b)


17
ahh giống như eq? vs bằng nhau? trong kế hoạch, có nó.
jottos

47
Hoặc ==so với .equals()trong Java. Phần tốt nhất là Python ==không giống với Java ==.
MatrixFrog

11
@ Toàn cầu: chỉ có một Nonegiá trị duy nhất . Vì vậy, nó luôn luôn có cùng một id.
SilentGhost

18
Điều này không giải quyết ví dụ "is -> True" của OP.
dùng2864740

6
@AlexanderSupertramp, vì thực tập chuỗi .
Chris Rico

570

Các câu trả lời khác ở đây là chính xác: isđược sử dụng để so sánh danh tính , trong khi ==được sử dụng để so sánh bình đẳng . Vì điều bạn quan tâm là sự bình đẳng (hai chuỗi nên chứa cùng một ký tự), nên trong trường hợp này, istoán tử chỉ đơn giản là sai và bạn nên sử dụng ==thay thế.

Lý do islàm việc tương tác là (hầu hết) chuỗi literals đang thực tập nội trú theo mặc định. Từ Wikipedia:

Chuỗi nội bộ tăng tốc độ so sánh chuỗi, đôi khi là một nút cổ chai hiệu năng trong các ứng dụng (như trình biên dịch và thời gian chạy ngôn ngữ lập trình động) phụ thuộc nhiều vào bảng băm với các khóa chuỗi. Không cần thực tập, kiểm tra hai chuỗi khác nhau có bằng nhau liên quan đến việc kiểm tra mọi ký tự của cả hai chuỗi. Điều này chậm vì nhiều lý do: nó vốn dĩ là O (n) trong độ dài của chuỗi; nó thường yêu cầu đọc từ một số vùng của bộ nhớ, mất thời gian; và các lần đọc sẽ lấp đầy bộ đệm của bộ xử lý, nghĩa là có ít bộ đệm hơn cho các nhu cầu khác. Với các chuỗi được thực hiện, một bài kiểm tra nhận dạng đối tượng đơn giản đủ sau thao tác thực tập ban đầu; điều này thường được thực hiện như một bài kiểm tra đẳng thức con trỏ,

Vì vậy, khi bạn có hai chuỗi ký tự (các từ được nhập theo nghĩa đen vào mã nguồn chương trình của bạn, được bao quanh bởi dấu ngoặc kép) trong chương trình của bạn có cùng giá trị, trình biên dịch Python sẽ tự động thực hiện các chuỗi, làm cho cả hai được lưu trữ như nhau vị trí bộ nhớ. (Lưu ý rằng điều này không phải lúc nào cũng xảy ra và các quy tắc khi điều này xảy ra khá phức tạp, vì vậy vui lòng không dựa vào hành vi này trong mã sản xuất!)

Vì trong phiên tương tác của bạn, cả hai chuỗi thực sự được lưu trữ trong cùng một vị trí bộ nhớ, chúng có cùng danh tính , do đó istoán tử hoạt động như mong đợi. Nhưng nếu bạn xây dựng một chuỗi bằng một số phương thức khác (ngay cả khi chuỗi đó chứa chính xác các ký tự), thì chuỗi đó có thể bằng nhau , nhưng nó không phải là cùng một chuỗi - đó là, nó có một danh tính khác , bởi vì nó là lưu trữ ở một nơi khác trong bộ nhớ.


6
Ai đó có thể đọc thêm về các quy tắc phức tạp khi chuỗi được thực hiện?
Noctis Skytower

89
+1 cho một lời giải thích kỹ lưỡng. Không chắc chắn làm thế nào câu trả lời khác nhận được rất nhiều upvote mà không giải thích những gì THỰC SỰ xảy ra.
That1Guy

4
đây chính xác là những gì tôi nghĩ khi đọc câu hỏi. Câu trả lời được chấp nhận là ngắn nhưng vẫn chứa đựng sự thật, nhưng câu trả lời này giải thích mọi thứ tốt hơn nhiều. Đẹp!
Sнаđошƒаӽ

3
@NoctisSkytower Googled tương tự và tìm thấy guilload.com/python-opes-iterning
xtreak

5
@ naught101: Không, quy tắc là chọn giữa ==isdựa trên loại kiểm tra bạn muốn. Nếu bạn quan tâm đến các chuỗi bằng nhau (nghĩa là có cùng nội dung) thì bạn nên luôn luôn sử dụng ==. Nếu bạn quan tâm đến việc có bất kỳ hai tên Python nào tham chiếu đến cùng một đối tượng không, bạn nên sử dụng is. Bạn có thể cần isnếu bạn đang viết mã xử lý nhiều giá trị khác nhau mà không quan tâm đến nội dung của chúng hoặc nếu không bạn chỉ biết có một thứ gì đó và bạn muốn bỏ qua các đối tượng khác giả vờ là điều đó. Nếu bạn không chắc chắn, luôn luôn chọn ==.
Daniel Pryden

108

Các istừ khóa là một thử nghiệm cho nhận dạng đối tượng trong khi ==là một sự so sánh giá trị.

Nếu bạn sử dụng is, kết quả sẽ đúng khi và chỉ khi đối tượng là cùng một đối tượng. Tuy nhiên, ==sẽ đúng bất cứ khi nào các giá trị của đối tượng là như nhau.


57

Một điều cuối cùng cần lưu ý, bạn có thể sử dụng sys.internhàm để đảm bảo rằng bạn nhận được một tham chiếu đến cùng một chuỗi:

>>> from sys import intern
>>> a = intern('a')
>>> a2 = intern('a')
>>> a is a2
True

Như đã chỉ ra ở trên, bạn không nên sử dụng isđể xác định sự bằng nhau của chuỗi. Nhưng điều này có thể hữu ích để biết nếu bạn có một số loại yêu cầu kỳ lạ để sử dụng is.

Lưu ý rằng internhàm được sử dụng để dựng sẵn trên Python 2 nhưng đã được chuyển sang sysmô-đun trong Python 3.


43

islà kiểm tra danh tính, ==là kiểm tra bình đẳng. Điều này có nghĩa là đó islà một cách để kiểm tra xem hai thứ là cùng một thứ, hay chỉ là tương đương.

Giả sử bạn có một personđối tượng đơn giản . Nếu nó được đặt tên là 'Jack' và '23', nó tương đương với một Jack 23yr khác, nhưng nó không phải là cùng một người.

class Person(object):
   def __init__(self, name, age):
       self.name = name
       self.age = age

   def __eq__(self, other):
       return self.name == other.name and self.age == other.age

jack1 = Person('Jack', 23)
jack2 = Person('Jack', 23)

jack1 == jack2 #True
jack1 is jack2 #False

Họ bằng tuổi nhau, nhưng họ không phải là cùng một người. Một chuỗi có thể tương đương với một chuỗi khác, nhưng nó không phải là cùng một đối tượng.


Nếu bạn thay đổi thiết lập jack1.age = 99, điều đó sẽ không thay đổi jack2.age. Đó là bởi vì họ là hai trường hợp khác nhau, vì vậy jack1 is not jack2. Tuy nhiên, họ có thể bằng nhau jack1 == jack2nếu tên và tuổi của họ giống nhau. Nó trở nên phức tạp hơn đối với các chuỗi, bởi vì các chuỗi là bất biến trong Python và Python thường sử dụng lại cùng một thể hiện. Tôi thích cách giải thích này vì nó sử dụng các trường hợp đơn giản (một đối tượng bình thường) thay vì các trường hợp đặc biệt (chuỗi).
Flimm

37

Đây là một ghi chú bên lề, nhưng trong python idiomatic, bạn sẽ thường thấy những thứ như:

if x is None: 
    # some clauses

Điều này là an toàn, vì được đảm bảo là một phiên bản của Đối tượng Null (tức là Không có) .


1
Điều này có đúng với Đúng và Sai không? Chỉ có một ví dụ như vậy sẽ phù hợp?
HandyManDan

1
@HandyManDan Vâng, họ là những người độc thân cả trong python 2 và 3.
kamillitw 16/11/18

@kamillitw nhưng trong Python 2 bạn có thể gán lại Sai và Đúng.
Martijn Pieters

28

Nếu bạn không chắc chắn mình đang làm gì, hãy sử dụng '=='. Nếu bạn có thêm một chút kiến ​​thức về nó, bạn có thể sử dụng 'là' cho các đối tượng đã biết như 'Không'.

Nếu không, bạn sẽ tự hỏi tại sao mọi thứ không hoạt động và tại sao điều này xảy ra:

>>> a = 1
>>> b = 1
>>> b is a
True
>>> a = 6000
>>> b = 6000
>>> b is a
False

Tôi thậm chí không chắc chắn nếu một số thứ được đảm bảo giữ nguyên giữa các phiên bản / triển khai python khác nhau.


1
Ví dụ thú vị cho thấy cách gán lại ints gây ra tình trạng này. Tại sao điều này thất bại? Là do thực tập hay cái gì khác?
Paul

Có vẻ như lý do trả về sai có thể là do triển khai trình thông dịch: stackoverflow.com/questions/132988/ mẹo
Paul


@ArchitJain Vâng, những liên kết đó giải thích nó khá tốt. Khi bạn đọc chúng, bạn sẽ biết những số bạn có thể sử dụng 'là' trên. Tôi chỉ mong họ sẽ giải thích lý do tại sao nó vẫn không phải là một ý tưởng tốt để làm điều đó :) Bạn biết điều này không làm cho nó là một ý tưởng tốt để giả sử mọi người khác cũng làm như vậy (hoặc phạm vi số nội bộ sẽ không bao giờ thay đổi)
Mattias Nilsson

20

Từ kinh nghiệm hạn chế của tôi với python, isđược sử dụng để so sánh hai đối tượng để xem liệu chúng có phải là cùng một đối tượng trái ngược với hai đối tượng khác nhau có cùng giá trị hay không. ==được sử dụng để xác định nếu các giá trị là giống hệt nhau.

Đây là một ví dụ tốt:

>>> s1 = u'public'
>>> s2 = 'public'
>>> s1 is s2
False
>>> s1 == s2
True

s1là một chuỗi unicode và s2là một chuỗi bình thường. Chúng không cùng loại, nhưng có cùng giá trị.


17

Tôi nghĩ rằng nó có liên quan đến thực tế là, khi so sánh 'là' đánh giá là sai, hai đối tượng riêng biệt được sử dụng. Nếu nó đánh giá là đúng, điều đó có nghĩa là bên trong nó sử dụng cùng một đối tượng chính xác và không tạo ra một đối tượng mới, có thể là do bạn đã tạo ra chúng trong vòng 2 giây hoặc hơn và vì không có khoảng cách thời gian lớn giữa nó được tối ưu hóa và sử dụng cùng một đối tượng.

Đây là lý do tại sao bạn nên sử dụng toán tử đẳng thức ==, chứ không phải isđể so sánh giá trị của một đối tượng chuỗi.

>>> s = 'one'
>>> s2 = 'two'
>>> s is s2
False
>>> s2 = s2.replace('two', 'one')
>>> s2
'one'
>>> s2 is s
False
>>> 

Trong ví dụ này, tôi đã tạo s2, một đối tượng chuỗi khác trước đây bằng 'một' nhưng nó không phải là cùng một đối tượng s, bởi vì trình thông dịch không sử dụng cùng một đối tượng như ban đầu tôi không gán nó cho 'một', nếu tôi có nó sẽ khiến chúng trở thành cùng một đối tượng.


3
Tuy nhiên, sử dụng .replace()như một ví dụ trong bối cảnh này có lẽ không phải là tốt nhất, bởi vì ngữ nghĩa của nó có thể gây nhầm lẫn. s2 = s2.replace()sẽ luôn tạo một đối tượng chuỗi mới , gán đối tượng chuỗi mới s2và sau đó loại bỏ đối tượng chuỗi s2được sử dụng để trỏ đến. Vì vậy, ngay cả khi bạn đã làm, s = s.replace('one', 'one')bạn vẫn sẽ nhận được một đối tượng chuỗi mới.
Daniel Pryden

13

Tôi tin rằng đây được gọi là chuỗi "thực tập". Python thực hiện điều này, Java cũng vậy và C và C ++ cũng vậy khi biên dịch ở các chế độ tối ưu hóa.

Nếu bạn sử dụng hai chuỗi giống nhau, thay vì lãng phí bộ nhớ bằng cách tạo hai đối tượng chuỗi, tất cả các chuỗi bên trong có cùng nội dung đều trỏ đến cùng một bộ nhớ.

Kết quả này trong toán tử Python "is" trả về True vì hai chuỗi có cùng nội dung đang trỏ vào cùng một đối tượng chuỗi. Điều này cũng sẽ xảy ra trong Java và C.

Điều này chỉ hữu ích cho tiết kiệm bộ nhớ mặc dù. Bạn không thể dựa vào nó để kiểm tra tính bằng nhau của chuỗi, bởi vì các trình thông dịch và trình biên dịch khác nhau và các công cụ JIT không thể luôn luôn làm điều đó.


12

Tôi đang trả lời câu hỏi mặc dù câu hỏi đã cũ vì không có câu trả lời nào ở trên trích dẫn tài liệu tham khảo ngôn ngữ

Trên thực tế, toán tử là kiểm tra danh tính và == toán tử kiểm tra sự bằng nhau,

Từ tài liệu tham khảo ngôn ngữ:

Các loại ảnh hưởng đến hầu hết tất cả các khía cạnh của hành vi đối tượng. Ngay cả tầm quan trọng của nhận dạng đối tượng cũng bị ảnh hưởng theo một số ý nghĩa: đối với các loại không thay đổi, các hoạt động tính toán các giá trị mới thực sự có thể trả về một tham chiếu đến bất kỳ đối tượng hiện có nào có cùng loại và giá trị, trong khi đối với các đối tượng có thể thay đổi thì điều này không được phép . Ví dụ: sau a = 1; b = 1, a và b có thể hoặc không thể tham chiếu đến cùng một đối tượng với giá trị một, tùy thuộc vào việc thực hiện, nhưng sau c = []; d = [], c và d được đảm bảo đề cập đến hai danh sách trống khác nhau, duy nhất, mới được tạo. (Lưu ý rằng c = d = [] gán cùng một đối tượng cho cả c và d.)

Vì vậy, từ tuyên bố trên, chúng ta có thể suy ra rằng các chuỗi là loại không thay đổi có thể thất bại khi được kiểm tra bằng "là" và có thể được kiểm tra thành công khi được kiểm tra bằng "là"

Áp dụng tương tự cho int, tuple cũng là loại không thay đổi


8

Các ==giá trị tương đương kiểm tra toán tử. Các isnhận dạng đối tượng kiểm tra khai thác, Python kiểm tra xem hai định nghĩa là cùng một đối tượng (ví dụ, sống tại cùng một địa chỉ trong bộ nhớ).

>>> a = 'banana'
>>> b = 'banana'
>>> a is b 
True

Trong ví dụ này, Python chỉ tạo một đối tượng chuỗi và cả hai abtham chiếu đến nó. Lý do là Python lưu trữ nội bộ và sử dụng lại một số chuỗi như một tối ưu hóa, thực sự chỉ có một chuỗi 'chuối' trong bộ nhớ, được chia sẻ bởi a và b; Để kích hoạt hành vi bình thường, bạn cần sử dụng chuỗi dài hơn:

>>> a = 'a longer banana'
>>> b = 'a longer banana'
>>> a == b, a is b
(True, False)

Khi bạn tạo hai danh sách, bạn nhận được hai đối tượng:

>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> a is b
False

Trong trường hợp này, chúng tôi sẽ nói rằng hai danh sách là tương đương, bởi vì chúng có cùng các yếu tố, nhưng không giống nhau, vì chúng không phải là cùng một đối tượng. Nếu hai đối tượng giống hệt nhau, chúng cũng tương đương, nhưng nếu chúng tương đương nhau, chúng không nhất thiết phải giống hệt nhau.

Nếu atham chiếu đến một đối tượng và bạn gán b = a, thì cả hai biến đều tham chiếu đến cùng một đối tượng:

>>> a = [1, 2, 3]
>>> b = a
>>> b is a
True

7

issẽ so sánh vị trí bộ nhớ. Nó được sử dụng để so sánh cấp đối tượng.

==sẽ so sánh các biến trong chương trình. Nó được sử dụng để kiểm tra ở mức giá trị.

is kiểm tra sự tương đương ở cấp địa chỉ

== kiểm tra sự tương đương mức giá trị


3

islà kiểm tra danh tính, ==là kiểm tra đẳng thức (xem Tài liệu Python ).

Trong hầu hết các trường hợp, nếu a is b, sau đó a == b. Nhưng có những trường hợp ngoại lệ, ví dụ:

>>> nan = float('nan')
>>> nan is nan
True
>>> nan == nan
False

Vì vậy, bạn chỉ có thể sử dụng ischo các bài kiểm tra danh tính, không bao giờ kiểm tra bằng.

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.