Hiểu toán tử “is” của Python


110

Các isnhà điều hành không phù hợp với các giá trị của các biến, nhưng các trường hợp bản thân.

Nó thực sự có nghĩa là gì?

Tôi đã khai báo hai biến được đặt tên xygán các giá trị giống nhau trong cả hai biến, nhưng nó trả về false khi tôi sử dụng istoán tử.

Tôi cần làm rõ. Đây là mã của tôi.

x = [1, 2, 3]
y = [1, 2, 3]

print(x is y)  # It prints false!

Câu trả lời:


181

Bạn đã hiểu sai những gì isnhà điều hành kiểm tra. Nó kiểm tra nếu hai biến trỏ đến cùng một đối tượng , không phải nếu hai biến có cùng giá trị.

Từ tài liệu dành cho isnhà điều hành :

Toán tử isis notkiểm tra nhận dạng đối tượng: x is yđúng nếu và chỉ khi xylà cùng một đối tượng.

Sử dụng ==toán tử thay thế:

print(x == y)

Bản in này True. xylà hai danh sách riêng biệt :

x[0] = 4
print(y)  # prints [1, 2, 3]
print(x == y)   # prints False

Nếu bạn sử dụng id()hàm, bạn sẽ thấy điều đó xycó các số nhận dạng khác nhau:

>>> id(x)
4401064560
>>> id(y)
4401098192

nhưng nếu bạn đã gán ycho xthì cả hai đều trỏ đến cùng một đối tượng:

>>> x = y
>>> id(x)
4401064560
>>> id(y)
4401064560
>>> x is y
True

ischo thấy cả hai đều là cùng một đối tượng, nó trả về True.

Hãy nhớ rằng trong Python, tên chỉ là nhãn tham chiếu đến giá trị ; bạn có thể có nhiều tên trỏ đến cùng một đối tượng. ischo bạn biết nếu hai tên trỏ đến một và cùng một đối tượng. ==cho bạn biết nếu hai tên tham chiếu đến các đối tượng có cùng giá trị.


13
Vì vậy, A is Bgiống như id(A) == id(B).
imallett

2
@imallett: đó là một proxy cho cùng một bài kiểm tra, miễn là bạn không lưu trữ id(A)trong một biến và sau này dự kiến variable == id(B)vẫn hoạt động; nếu Ađã bị xóa trong thời gian chờ đợi thì Bcó thể đã được cấp cùng một vị trí bộ nhớ.
Martijn Pieters

1
Không thể định dạng trong nhận xét. Nhưng có một điều thú vị. :) >>> x = 5 \n>>> y = 5 \n>>> x là y \nĐúng \n>>> x == y \nĐúng \n>>>\n
Haranadh 6/617

5
Các số nguyên nhỏ được thực hiện trong CPython vì chúng được sử dụng thường xuyên. Đó là một sự tối ưu hóa. x = 5; y = 5; x là y => Đúng vì id (x) == id (y). Đó là cùng một đối tượng số nguyên được sử dụng lại. Hoạt động bằng Python vì số nguyên là bất biến. Nếu bạn làm x = 1,0; y = 1.0 hoặc x = 9999; y = 9999, nó sẽ không có cùng danh tính, bởi vì float và int lớn hơn không được thực hiện.
Magnus Lyckå

1
@ MagnusLyckå có những cách tối ưu hóa khác có thể khiến các đối tượng không thể thay đổi được lưu vào bộ nhớ đệm. Ví dụ: nếu bạn chạy ví dụ của mình trong một hàm mới hoặc cùng với dấu chấm phẩy phân tách trong trình thông dịch tương tác, bạn sẽ thấy chúng cũng có cùng một id.
Martijn Pieters

60

Một bản sao khác đã hỏi tại sao hai chuỗi bằng nhau thường không giống nhau, điều này thực sự không được trả lời ở đây:

>>> x = 'a' 
>>> x += 'bc'
>>> y = 'abc'
>>> x == y
True
>>> x is y
False

Vì vậy, tại sao chúng không cùng một chuỗi? Đặc biệt là với điều này:

>>> z = 'abc'
>>> w = 'abc'
>>> z is w
True

Hãy tạm dừng phần thứ hai một chút. Làm thế nào cái đầu tiên có thể đúng?

Trình thông dịch sẽ phải có một "interning table", một bảng ánh xạ các giá trị chuỗi với các đối tượng chuỗi, vì vậy mỗi khi bạn cố gắng tạo một chuỗi mới với nội dung 'abc', bạn sẽ nhận lại cùng một đối tượng. Wikipedia có một cuộc thảo luận chi tiết hơn về cách hoạt động của interning.

Và Python một bảng xen kẽ chuỗi; bạn có thể thực hiện thủ công các chuỗi bằng sys.internphương pháp này.

Trên thực tế, Python được phép tự động thực hiện bất kỳ kiểu bất biến nào, nhưng không bắt buộc phải làm như vậy. Các cách triển khai khác nhau sẽ đan xen các giá trị khác nhau.

CPython (triển khai bạn đang sử dụng nếu bạn không biết mình đang sử dụng triển khai nào) tự động thực hiện các số nguyên nhỏ và một số đơn lẻ đặc biệt như False, nhưng không phải chuỗi (hoặc số nguyên lớn, hoặc bộ giá trị nhỏ hoặc bất kỳ thứ gì khác). Bạn có thể thấy điều này khá dễ dàng:

>>> a = 0
>>> a += 1
>>> b = 1
>>> a is b
True
>>> a = False
>>> a = not a
>>> b = True
a is b
True
>>> a = 1000
>>> a += 1
>>> b = 1001
>>> a is b
False

OK, nhưng tại sao đã zwgiống hệt nhau?

Đó không phải là trình thông dịch tự động thực hiện, đó là các giá trị gấp của trình biên dịch.

Nếu cùng một chuỗi thời gian biên dịch xuất hiện hai lần trong cùng một module (những gì chính xác phương tiện này rất khó để xác định-nó không phải là điều tương tự như một chữ chuỗi, vì r'abc', 'abc''a' 'b' 'c'tất cả đều literals khác nhau nhưng cùng một chuỗi nhưng dễ hiểu trực quan), trình biên dịch sẽ chỉ tạo một thể hiện của chuỗi, với hai tham chiếu.

Trên thực tế, trình biên dịch còn có thể tiến xa hơn nữa: 'ab' + 'c'có thể được chuyển đổi thành 'abc'bởi trình tối ưu hóa, trong trường hợp đó, nó có thể được gấp lại cùng với một 'abc'hằng số trong cùng một mô-đun.

Một lần nữa, đây là điều mà Python được phép nhưng không bắt buộc phải làm. Nhưng trong trường hợp này, CPython luôn gấp các dây nhỏ (và cũng có thể, ví dụ, các bộ nhỏ). (Mặc dù trình biên dịch theo câu lệnh của trình thông dịch tương tác không chạy tối ưu hóa tương tự như trình biên dịch mô-đun tại một thời điểm, vì vậy bạn sẽ không thấy chính xác các kết quả tương tác.)


Vì vậy, bạn nên làm gì về điều này với tư cách là một lập trình viên?

Cũng không có gì. Bạn hầu như không bao giờ có bất kỳ lý do gì để quan tâm xem hai giá trị bất biến có giống hệt nhau hay không. Nếu bạn muốn biết khi nào bạn có thể sử dụng a is bthay thế a == b, bạn đang đặt câu hỏi sai. Chỉ luôn sử dụng a == bngoại trừ trong hai trường hợp:

  • Đối với các so sánh dễ đọc hơn với các giá trị singleton như x is None.
  • Đối với các giá trị có thể thay đổi, khi bạn cần biết liệu đột biến xsẽ ảnh hưởng đến y.

1
Lời giải thích tuyệt vời, đặc biệt là lời khuyên của bạn ở cuối.
DavidG

Cảm ơn bạn đã giải thích chi tiết đó. Ai đó có biết: nếu wzgiống nhau vì các giá trị gấp của trình biên dịch, tại sao điều này cũng hoạt động trong REPL, thậm chí sử dụng id()để kiểm tra các tham chiếu? Sử dụng REPL trên Python 3.7
Chi-chi

8

ischỉ trả về true nếu chúng thực sự là cùng một đối tượng. Nếu chúng giống nhau, một thay đổi đối với một cũng sẽ hiển thị trong khác. Đây là một ví dụ về sự khác biệt.

>>> x = [1, 2, 3]
>>> y = [1, 2, 3]
>>> print x is y
False
>>> z = y
>>> print y is z
True
>>> print x is z
False
>>> y[0] = 5
>>> print z
[5, 2, 3]

8

Được gợi ý bởi một câu hỏi trùng lặp , phép tương tự này có thể hoạt động:

# - Darling, I want some pudding!
# - There is some in the fridge.

pudding_to_eat = fridge_pudding
pudding_to_eat is fridge_pudding
# => True

# - Honey, what's with all the dirty dishes?
# - I wanted to eat pudding so I made some. Sorry about the mess, Darling.
# - But there was already some in the fridge.

pudding_to_eat = make_pudding(ingredients)
pudding_to_eat is fridge_pudding
# => False

3
Có thể chỉ là sở thích cá nhân (không có ý định chơi chữ) nhưng tôi thấy sự tương tự này khó hiểu hơn là hữu ích và khiến tôi muốn ăn bánh pudding khi tôi không có cái nào trong tủ lạnh: (Tôi nghĩ câu trả lời của Mark Ransom, mặc dù nhàm chán hơn, là có lẽ mang tính hướng dẫn nhiều hơn
Tom Đóng

1
@TomClose: Có rất nhiều câu trả lời tốt cho câu hỏi này, đủ để có không gian cho sự ngắn gọn. Ngoài ra, tôi cũng muốn bánh pudding.
Amadan

5

isis notlà hai toán tử nhận dạng trong Python. istoán tử không so sánh giá trị của các biến, nhưng so sánh danh tính của các biến. Xem xét điều này:

>>> a = [1,2,3]
>>> b = [1,2,3]
>>> hex(id(a))
'0x1079b1440'
>>> hex(id(b))
'0x107960878'
>>> a is b
False
>>> a == b
True
>>>

Ví dụ trên cho bạn thấy rằng danh tính (cũng có thể là địa chỉ bộ nhớ trong Cpython) là khác nhau đối với cả ab(mặc dù giá trị của chúng giống nhau). Đó là lý do tại sao khi bạn nói a is bnó trả về false do sự không khớp trong danh tính của cả hai toán hạng. Tuy nhiên, khi bạn nói a == b, nó trả về true vì ==hoạt động chỉ xác minh xem cả hai toán hạng có cùng giá trị được gán cho chúng hay không.

Ví dụ thú vị (cho lớp bổ sung):

>>> del a
>>> del b
>>> a = 132
>>> b = 132
>>> hex(id(a))
'0x7faa2b609738'
>>> hex(id(b))
'0x7faa2b609738'
>>> a is b
True
>>> a == b
True
>>>

Trong ví dụ trên, mặc dù ablà hai biến khác nhau, được a is btrả về True. Điều này là do loại aintđó là một đối tượng không thay đổi. Vì vậy, python (tôi đoán là để tiết kiệm bộ nhớ) đã cấp phát cùng một đối tượng bkhi nó được tạo với cùng một giá trị. Vì vậy, trong trường hợp này, danh tính của các biến đã khớp và a is bhóa ra là như vậy True.

Điều này sẽ áp dụng cho tất cả các đối tượng không thể thay đổi:

>>> del a
>>> del b
>>> a = "asd"
>>> b = "asd"
>>> hex(id(a))
'0x1079b05a8'
>>> hex(id(b))
'0x1079b05a8'
>>> a is b
True
>>> a == b
True
>>>

Hy vọng rằng sẽ giúp.


đây là một ví dụ thực sự tốt đẹp. cảm ơn vì thông tin chi tiết.
Haranadh

Nhưng hãy thử a = 123456789 b = 123456789
user2183078

Mọi thứ nhỏ hơn -5hoặc cao hơn 256trong Python sẽ là Sai. Python lưu trữ các số trong phạm vi [-5, 256].
thông minh

Không phải tất cả các đối tượng bất biến sẽ được chia sẻ như bạn hiển thị, đó là một cách tối ưu hóa được áp dụng bởi thời gian chạy Python cho một số đối tượng chứ không phải những đối tượng khác. Quá trình chia sẻ các số nguyên nhỏ được ghi lại đầy đủ, nhưng tôi không nghĩ rằng nó dành cho đan chuỗi .
Mark Ransom

4

x is ygiống như id(x) == id(y), so sánh danh tính của các đối tượng.

Như @ tomasz-kurgan đã chỉ ra trong nhận xét bên dưới, istoán tử hành xử bất thường với một số đối tượng nhất định.

Ví dụ

>>> class A(object):
...   def foo(self):
...     pass
... 
>>> a = A()
>>> a.foo is a.foo
False
>>> id(a.foo) == id(a.foo)
True

Tham chiếu;
https://docs.python.org/2/reference/expressions.html#is-not
https://docs.python.org/2/reference/expressions.html#id24


Không phải đâu. Nó có thể hoạt động tương tự trong hầu hết các trường hợp, nhưng không phải lúc nào cũng đúng. Hãy xem điều này - ở cuối trang, dấu đầu dòng 6.:> (...), bạn có thể nhận thấy hành vi dường như bất thường trong một số cách sử dụng nhất định của toán tử is , như những hành vi liên quan đến so sánh giữa các phương thức phiên bản hoặc hằng số Và ví dụ làm việc tối thiểu : `class A (object): def foo (self): pass a = A () print a.foo là a.foo print id (a.foo) == id (a.foo)`
Tomasz Kurgan

3

Như bạn có thể kiểm tra ở đây đến một số nguyên nhỏ. Các số trên 257 không phải là một số nguyên nhỏ, vì vậy nó được tính như một đối tượng khác.

Tốt hơn là sử dụng == thay thế trong trường hợp này.

Thông tin thêm tại đây: http://docs.python.org/2/c-api/int.html


2

X trỏ đến một mảng, Y trỏ đến một mảng khác. Các mảng đó giống hệt nhau, nhưng isnhà điều hành sẽ xem xét các con trỏ đó, chúng không giống nhau.


5
Python không có con trỏ. Bạn cần thắt chặt thuật ngữ của mình.
David Heffernan,

3
Nó hoạt động bên trong, giống như Java và nhiều ngôn ngữ khác. Trên thực tế, ischức năng của nhà điều hành cho thấy điều này.
Neko

5
Các chi tiết thực hiện không phải là điều quan trọng. Tài liệu sử dụng thuật ngữ "nhận dạng đối tượng". Vậy bạn nên. "Các toán tử được và không được kiểm tra tính đồng nhất của đối tượng: x là y đúng nếu và chỉ khi x và y là cùng một đối tượng. X không phải là y mang lại giá trị chân lý nghịch đảo."
David Heffernan,

1
@Neko: CPython nội bộ sử dụng con trỏ. Nhưng rõ ràng Jython (được triển khai trong Java) và PyPy (được triển khai trong một tập con của Python) không sử dụng con trỏ. Trong PyPy, một số đối tượng thậm chí sẽ không có dấu idtrừ khi bạn yêu cầu.
abarnert

1

Nó so sánh danh tính đối tượng, nghĩa là các biến có tham chiếu đến cùng một đối tượng trong bộ nhớ hay không. Nó giống như ==trong Java hoặc C (khi so sánh các con trỏ).


1

Một ví dụ đơn giản với trái cây

fruitlist = [" apple ", " banana ", " cherry ", " durian "]
newfruitlist = fruitlist
verynewfruitlist = fruitlist [:]
print ( fruitlist is newfruitlist )
print ( fruitlist is verynewfruitlist )
print ( newfruitlist is verynewfruitlist )

Đầu ra:

True
False
False

Nếu bạn cố gắng

fruitlist = [" apple ", " banana ", " cherry ", " durian "]
newfruitlist = fruitlist
verynewfruitlist = fruitlist [:]
print ( fruitlist == newfruitlist )
print ( fruitlist == verynewfruitlist )
print ( newfruitlist == verynewfruitlist )

Đầu ra khác nhau:

True
True
True

Đó là bởi vì toán tử == chỉ so sánh nội dung của biến. Để so sánh bản sắc của 2 sử dụng biến những toán tử

Để in số nhận dạng:

print ( id( variable ) )

-3

Nhà isđiều hành không có gì khác ngoài một phiên bản tiếng Anh của ==. Vì ID của hai danh sách khác nhau nên câu trả lời là sai. Bạn co thể thử:

a=[1,2,3]
b=a
print(b is a )#True

* Bởi vì ID của cả hai danh sách sẽ giống nhau


iskhông phải là 'phiên bản tiếng Anh của =='
David Buck
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.