ทำความเข้าใจกับโอเปอเรเตอร์“ is” ของ Python


110

ตัวisดำเนินการไม่ตรงกับค่าของตัวแปร แต่อินสแตนซ์เอง

แท้จริงแล้วหมายความว่าอย่างไร?

ฉันประกาศตัวแปรสองตัวที่ตั้งชื่อxและyกำหนดค่าเดียวกันในทั้งสองตัวแปร แต่มันกลับเป็นเท็จเมื่อฉันใช้ตัวisดำเนินการ

ฉันต้องการคำชี้แจง นี่คือรหัสของฉัน

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

print(x is y)  # It prints false!

คำถามที่เกี่ยวข้องstackoverflow.com/questions/38189660/…
Kasravnd

คำตอบ:


181

คุณเข้าใจผิดว่าตัวisดำเนินการทดสอบอะไร จะทดสอบว่าตัวแปรสองตัวชี้ไปที่วัตถุเดียวกันหรือไม่หากตัวแปรสองตัวมีค่าเท่ากัน

จากเอกสารสำหรับตัวisดำเนินการ :

ตัวดำเนินการisและis notทดสอบเอกลักษณ์ของอ็อบเจ็กต์: x is yเป็นจริงในกรณีที่xและyเป็นวัตถุเดียวกัน

ใช้ตัว==ดำเนินการแทน:

print(x == y)

Trueพิมพ์นี้ xและyเป็นสองรายการแยกกัน:

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

หากคุณใช้id()ฟังก์ชันนี้คุณจะเห็นสิ่งนั้นxและyมีตัวระบุที่แตกต่างกัน:

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

แต่ถ้าคุณจะกำหนดyให้xทั้งสองชี้ไปที่วัตถุเดียวกัน:

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

และแสดงให้เห็นว่าทั้งสองมีวัตถุเดียวกันก็จะส่งกลับisTrue

โปรดจำไว้ว่าใน Python ชื่อเป็นเพียงป้ายกำกับที่อ้างอิงค่าเท่านั้น คุณสามารถมีหลายชื่อชี้ไปที่วัตถุเดียวกัน isบอกคุณว่าชื่อสองชื่อชี้ไปที่หนึ่งและวัตถุเดียวกันหรือไม่ ==บอกคุณว่าสองชื่ออ้างถึงออบเจ็กต์ที่มีค่าเท่ากันหรือไม่


13
ดังนั้นเป็นเช่นเดียวกับA is B id(A) == id(B)
imallett

2
@imallett: เป็นพร็อกซีสำหรับการทดสอบเดียวกันหากคุณไม่ได้เก็บไว้id(A)ในตัวแปรและคาดว่าvariable == id(B)จะยังใช้งานได้ในภายหลัง หากAถูกลบในระหว่างBนี้อาจได้รับตำแหน่งหน่วยความจำเดียวกัน
Martijn Pieters

1
ไม่สามารถจัดรูปแบบในความคิดเห็น แต่มีสิ่งที่น่าสนใจคือ :) >>> x = 5 \n>>> y = 5 \n>>> x is y \nTrue \n>>> x == y \nTrue \n>>>\n
Haranadh

5
จำนวนเต็มขนาดเล็กอยู่ภายใน CPython เนื่องจากมีการใช้บ่อย เป็นการเพิ่มประสิทธิภาพ x = 5; y = 5; x คือ y => จริงเพราะ id (x) == id (y) เป็นอ็อบเจ็กต์จำนวนเต็มเดียวกันซึ่งถูกนำมาใช้ใหม่ ทำงานใน Python เนื่องจากจำนวนเต็มไม่เปลี่ยนรูป ถ้าคุณทำ x = 1.0; y = 1.0 หรือ x = 9999; y = 9999 มันจะไม่เป็นเอกลักษณ์เดียวกันเพราะลอยและ int ขนาดใหญ่ไม่ได้อยู่ภายใน
Magnus Lyckå

1
@ MagnusLyckåมีการเพิ่มประสิทธิภาพอื่น ๆ ที่สามารถทำให้วัตถุที่ไม่เปลี่ยนรูปถูกแคชได้ ตัวอย่างเช่นหากคุณเรียกใช้ตัวอย่างของคุณในฟังก์ชันใหม่หรือใช้ร่วมกับอัฒภาคคั่นในล่ามแบบโต้ตอบคุณจะพบว่ามีรหัสเดียวกันด้วย
Martijn Pieters

60

รายการที่ซ้ำกันอีกรายการหนึ่งถามว่าทำไมโดยทั่วไปสองสตริงที่เท่ากันจึงไม่เหมือนกันซึ่งยังไม่มีคำตอบที่นี่:

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

แล้วทำไมมันไม่เหมือนกันล่ะ? โดยเฉพาะอย่างยิ่งสิ่งนี้:

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

ขอปิดส่วนที่สองสักหน่อย คนแรกจะเป็นจริงได้อย่างไร?

ล่ามจะต้องมี "interning table" ซึ่งเป็นค่าสตริงการแม็ปตารางกับสตริงอ็อบเจ็กต์ดังนั้นทุกครั้งที่คุณพยายามสร้างสตริงใหม่พร้อมเนื้อหา'abc'คุณจะได้รับอ็อบเจกต์เดียวกันกลับคืนมา Wikipediaมีการอภิปรายรายละเอียดเพิ่มเติมเกี่ยวกับวิธีการฝึกงาน

และ Python มีตารางภายในสตริง คุณสามารถฝึกสตริงด้วยตนเองด้วยsys.internวิธีการ

ในความเป็นจริง Python ได้รับอนุญาตให้ควบคุมประเภทที่ไม่เปลี่ยนรูปได้โดยอัตโนมัติ แต่ไม่จำเป็นต้องทำเช่นนั้น การใช้งานที่แตกต่างกันจะทำให้เกิดคุณค่าที่แตกต่างกัน

CPython (การใช้งานที่คุณใช้หากคุณไม่ทราบว่าคุณกำลังใช้การนำไปใช้งานใด) auto-interns จำนวนเต็มขนาดเล็กและ singletons พิเศษบางอย่างเช่นFalseแต่ไม่ใช่สตริง (หรือจำนวนเต็มขนาดใหญ่หรือสิ่งที่มีขนาดเล็กหรือสิ่งอื่นใด) คุณสามารถเห็นสิ่งนี้ได้อย่างง่ายดาย:

>>> 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 แต่ทำไมอยู่zและwที่เหมือนกัน?

นั่นไม่ใช่ล่ามโดยอัตโนมัติภายในนั่นคือค่าการพับของคอมไพเลอร์

ถ้าสตริงรวบรวมเวลาเดียวกันปรากฏขึ้นเป็นครั้งที่สองในโมดูลเดียวกัน (สิ่งที่ว่านี้หมายถึงเป็นเรื่องยากที่จะกำหนดมันไม่ได้เป็นสิ่งเดียวกับตัวอักษรสตริงเพราะr'abc', 'abc'และ'a' 'b' 'c'มีตัวอักษรที่แตกต่างกันทั้งหมด แต่เดียวกันสตริง แต่ง่ายต่อการเข้าใจ โดยสัญชาตญาณ) คอมไพเลอร์จะสร้างสตริงเพียงหนึ่งอินสแตนซ์โดยมีการอ้างอิงสองรายการ

ในความเป็นจริงคอมไพเลอร์สามารถไปได้ไกลกว่า'ab' + 'c'นั้น: สามารถแปลงเป็นได้'abc'โดยเครื่องมือเพิ่มประสิทธิภาพซึ่งในกรณีนี้สามารถพับพร้อมกับ'abc'ค่าคงที่ในโมดูลเดียวกันได้

อีกครั้งนี่เป็นสิ่งที่อนุญาตให้ใช้ Python แต่ไม่จำเป็นต้องทำ แต่ในกรณีนี้ CPython จะพับสตริงเล็ก ๆ เสมอ (และเช่นสิ่งที่มีขนาดเล็ก) (แม้ว่าคอมไพลเลอร์คำสั่งต่อคำสั่งของล่ามแบบโต้ตอบจะไม่เรียกใช้การปรับให้เหมาะสมเช่นเดียวกับคอมไพเลอร์โมดูลในแต่ละครั้งดังนั้นคุณจะไม่เห็นผลลัพธ์เดียวกันในเชิงโต้ตอบ)


แล้วคุณควรทำอย่างไรกับเรื่องนี้ในฐานะโปรแกรมเมอร์?

อืม…ไม่มีอะไร คุณแทบไม่มีเหตุผลใด ๆ ที่จะต้องสนใจว่าค่าที่ไม่เปลี่ยนรูปสองค่าจะเหมือนกันหรือไม่ หากคุณต้องการทราบว่าคุณสามารถใช้a is bแทนได้เมื่อa == bใดคุณกำลังถามคำถามผิด ใช้เสมอa == bยกเว้นในสองกรณี:

  • สำหรับการเปรียบเทียบที่อ่านได้เพิ่มเติมกับค่าซิงเกิลตันเช่นx is None.
  • สำหรับค่าที่ไม่แน่นอนเมื่อคุณต้องการทราบว่าการกลายพันธุ์xจะส่งผลต่อไฟล์y.

1
คำอธิบายที่ยอดเยี่ยมโดยเฉพาะคำแนะนำของคุณในตอนท้าย
DavidG

ขอบคุณสำหรับคำอธิบายโดยละเอียด ไม่ใครสักคนที่รู้: ถ้าwและzเหมือนกันเพราะคอมไพเลอร์พับค่าทำไมยังทำงานใน REPL แม้ใช้id()ในการตรวจสอบการอ้างอิงหรือไม่ การใช้ REPL บน Python 3.7
Chi-chi

8

isคืนค่าจริงก็ต่อเมื่อเป็นวัตถุเดียวกันเท่านั้น หากเหมือนกันการเปลี่ยนแปลงในรายการหนึ่งจะปรากฏขึ้นในอีกรายการหนึ่งด้วย นี่คือตัวอย่างของความแตกต่าง

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

เมื่อมีคำถามซ้ำกันการเปรียบเทียบนี้อาจใช้ได้ผล:

# - 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
อาจเป็นเพียงรสนิยมส่วนตัว (ไม่ได้ตั้งใจเล่นสำนวน) แต่ฉันพบว่าการเปรียบเทียบนี้สับสนมากกว่าเป็นประโยชน์และทำให้ฉันอยากกินพุดดิ้งเมื่อไม่มีในตู้เย็น :( ฉันคิดว่าคำตอบของ Mark Ransom แม้ว่าจะน่าเบื่อกว่าก็คือ อาจให้คำแนะนำได้มากกว่า
Tom Close

1
@ TomClose: มีคำตอบที่ดีมากมายสำหรับคำถามนี้เพียงพอที่จะมีที่ว่างสำหรับความคะนอง นอกจากนี้ฉันต้องการพุดดิ้งด้วย
Amadan

5

isและis notเป็นตัวดำเนินการสองตัวใน Python isตัวดำเนินการไม่ได้เปรียบเทียบค่าของตัวแปร แต่จะเปรียบเทียบข้อมูลประจำตัวของตัวแปร พิจารณาสิ่งนี้:

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

ตัวอย่างข้างต้นแสดงให้คุณเห็นว่าข้อมูลประจำตัว (อาจเป็นที่อยู่หน่วยความจำใน Cpython ก็ได้) แตกต่างกันสำหรับทั้งสองaและb(แม้ว่าค่าจะเหมือนกันก็ตาม) นั่นคือเหตุผลที่เมื่อคุณบอกว่าa is bมันส่งคืนค่าเท็จเนื่องจากความไม่ตรงกันในข้อมูลประจำตัวของตัวถูกดำเนินการทั้งสอง อย่างไรก็ตามเมื่อคุณพูดa == bมันจะคืนค่าจริงเนื่องจากการ==ดำเนินการจะตรวจสอบว่าตัวถูกดำเนินการทั้งสองมีค่าเดียวกันที่กำหนดให้

ตัวอย่างที่น่าสนใจ (สำหรับเกรดพิเศษ):

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

ในตัวอย่างข้างต้นแม้ว่าaและbสองตัวแปรที่แตกต่างกันกลับa is b Trueเนื่องจากชนิดของaคือintวัตถุที่ไม่เปลี่ยนรูป ดังนั้น python (ฉันเดาว่าจะบันทึกหน่วยความจำ) จึงจัดสรรวัตถุเดียวกันให้bเมื่อมันถูกสร้างขึ้นด้วยค่าเดียวกัน ดังนั้นในกรณีนี้ตนของตัวแปรที่ตรงและเปิดออกมาเป็นa is bTrue

สิ่งนี้จะใช้กับวัตถุที่ไม่เปลี่ยนรูปทั้งหมด:

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

หวังว่าจะช่วยได้


นี่เป็นตัวอย่างที่ดีจริงๆ ขอบคุณสำหรับข้อมูลโดยละเอียด
Haranadh

แต่ลอง a = 123456789 b = 123456789
user2183078

ทุกอย่างที่น้อยกว่า-5หรือสูงกว่า256ใน Python จะเป็นเท็จ Python แคชตัวเลขในช่วง [-5, 256]
สมาร์ท

ไม่สามารถแชร์วัตถุที่ไม่เปลี่ยนรูปได้ทั้งหมดตามที่คุณแสดงนั่นคือการเพิ่มประสิทธิภาพที่ใช้โดยรันไทม์ Python สำหรับวัตถุบางอย่าง แต่ไม่ใช่วัตถุอื่น กระบวนการของการแบ่งปันจำนวนเต็มขนาดเล็กเป็นเอกสารที่ดี แต่ผมไม่คิดว่ามันเป็นสตริง interning
Mark Ransom

4

x is yเหมือนกับการid(x) == id(y)เปรียบเทียบเอกลักษณ์ของวัตถุ

ดังที่ @ tomasz-kurgan ชี้ให้เห็นในความคิดเห็นด้านล่างisตัวดำเนินการทำงานผิดปกติกับวัตถุบางอย่าง

เช่น

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

อ้างอิง;
https://docs.python.org/2/reference/expressions.html#is-not
https://docs.python.org/2/reference/expressions.html#id24


ไม่มันไม่ได้ อาจมีพฤติกรรมคล้ายกันในกรณีส่วนใหญ่ แต่ก็ไม่จริงเสมอไป ดูสิ่งนี้ - ด้านล่างสุดของหน้าหัวข้อย่อย 6:> (... ) คุณอาจสังเกตเห็นพฤติกรรมที่ดูผิดปกติในการใช้ตัวดำเนินการisบางอย่างเช่นการเปรียบเทียบระหว่างวิธีการของอินสแตนซ์หรือค่าคงที่และตัวอย่างการทำงานขั้นต่ำ : `class A (object): def foo (self): pass a = A () print a.foo is a.foo print id (a.foo) == id (a.foo)`
Tomasz Kurgan

3

ดังที่คุณสามารถตรวจสอบจำนวนเต็มขนาดเล็กได้ที่นี่ ตัวเลขที่สูงกว่า 257 ไม่ใช่ int ขนาดเล็กดังนั้นจึงคำนวณเป็นวัตถุอื่น

แนะนำให้ใช้==แทนในกรณีนี้จะดีกว่า

ข้อมูลเพิ่มเติมอยู่ที่นี่: http://docs.python.org/2/c-api/int.html


2

X ชี้ไปที่อาร์เรย์ Y ชี้ไปยังอาร์เรย์อื่น อาร์เรย์เหล่านั้นเหมือนกัน แต่ตัวisดำเนินการจะดูพอยน์เตอร์เหล่านั้นซึ่งไม่เหมือนกัน


5
Python ไม่มีพอยน์เตอร์ คุณต้องกระชับคำศัพท์ของคุณ
David Heffernan

3
มันทำงานภายในเช่นเดียวกับ Java และภาษาอื่น ๆ อีกมากมาย ในความเป็นจริงisการทำงานของตัวดำเนินการแสดงสิ่งนี้
Neko

5
รายละเอียดการดำเนินการไม่ใช่สิ่งที่สำคัญ เอกสารประกอบใช้คำศัพท์เฉพาะ "object identity" คุณควร "ตัวดำเนินการคือและไม่ได้ทดสอบเอกลักษณ์ของอ็อบเจ็กต์: x คือ y เป็นจริงก็ต่อเมื่อ x และ y เป็นอ็อบเจ็กต์เดียวกันเท่านั้น x ไม่ให้ค่าความจริงผกผัน"
David Heffernan

1
@Neko: CPython ใช้พอยน์เตอร์ภายใน แต่เห็นได้ชัดว่า Jython (ใช้งานใน Java) และ PyPy (ใช้งานในส่วนย่อยของ Python) ไม่ใช้พอยน์เตอร์ ใน PyPy วัตถุบางอย่างจะไม่มีด้วยซ้ำidเว้นแต่คุณจะร้องขอ
ยกเลิก

1

มันเปรียบเทียบเอกลักษณ์ของวัตถุนั่นคือว่าตัวแปรอ้างถึงวัตถุเดียวกันในหน่วยความจำหรือไม่ มันเหมือนกับ==ใน Java หรือ C (เมื่อเปรียบเทียบพอยน์เตอร์)


1

ตัวอย่างง่ายๆกับผลไม้

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

เอาท์พุต:

True
False
False

ถ้าคุณพยายาม

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

ผลลัพธ์แตกต่างกัน:

True
True
True

นั่นเป็นเพราะตัวดำเนินการ == เปรียบเทียบเฉพาะเนื้อหาของตัวแปร ในการเปรียบเทียบอัตลักษณ์ของตัวแปร 2 ตัวให้ใช้ตัวดำเนินการis

ในการพิมพ์หมายเลขประจำตัว:

print ( id( variable ) )

-3

ตัวisดำเนินการคืออะไร แต่เป็นเวอร์ชันภาษาอังกฤษของ==. เนื่องจาก ID ของทั้งสองรายการแตกต่างกันดังนั้นคำตอบจึงเป็นเท็จ คุณสามารถลอง:

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

* เนื่องจาก ID ของทั้งสองรายการจะเหมือนกัน


isไม่ใช่ 'เวอร์ชันภาษาอังกฤษ=='
David Buck
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.