ทำไมการเปรียบเทียบสตริงโดยใช้ '==' หรือ 'คือ' บางครั้งให้ผลลัพธ์ที่แตกต่างกัน


1146

'public'ฉันมีโปรแกรมหลามที่สองตัวแปรมีการตั้งค่า ในการแสดงออกตามเงื่อนไขที่ฉันมีการเปรียบเทียบvar1 is var2ที่ล้มเหลว แต่ถ้าผมเปลี่ยนไปก็จะส่งกลับvar1 == var2True

ตอนนี้ถ้าฉันเปิด Python interpreter และทำแบบเดียวกันคือการเปรียบเทียบก็สำเร็จ

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

ฉันหายไปนี่อะไร



3
input = raw_input("Decide (y/n): ")ปัญหานี้ยังเกิดขึ้นเมื่อคุณอ่านการป้อนข้อมูลผ่านทางคอนโซลเช่น: ในกรณีนี้การป้อนข้อมูลของ "y" และif input == 'y':จะส่งกลับ "True" ในขณะที่if input is 'y':จะกลับเท็จ
Semjon Mössinger

4
บล็อกนี้ให้คำอธิบายที่สมบูรณ์มากกว่าคำตอบใด ๆguilload.com/python-string-interning
Chris_Rands

1
ในฐานะที่ @ chris-rico กล่าวถึงฉันอธิบายได้ดีที่นี่stackoverflow.com/q/15541404/1695680
ThorSummoner

คำตอบ:


1533

isคือการทดสอบตัวตน==คือการทดสอบความเท่าเทียมกัน สิ่งที่เกิดขึ้นในรหัสของคุณจะถูกจำลองในล่ามเช่นนี้:

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

ดังนั้นไม่น่าแปลกใจที่พวกเขาไม่เหมือนกันใช่มั้ย

ในคำอื่น ๆ : isเป็นid(a) == id(b)


17
อ่าเช่นเดียวกับ eq? เทียบกับ? ในรูปแบบรับมัน
jottos

47
หรือ==vs .equals()ใน Java ส่วนที่ดีที่สุดคือการที่งูใหญ่==ไม่ได้คล้ายคลึงกับ ==Java
MatrixFrog

11
@ Крайст: มีเพียงNoneค่าเดียว ดังนั้นจึงมีรหัสเดียวกันเสมอ
SilentGhost

18
สิ่งนี้ไม่ได้ระบุถึงตัวอย่างของ OP คือ "> - True"
2864740

6
@AlexanderSupertramp เพราะสตริง interning
Chris Rico

569

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

เหตุผลที่isทำงานแบบโต้ตอบคือตัวอักษรสตริง (ส่วนใหญ่) จะถูกฝึกงานตามค่าเริ่มต้น จาก Wikipedia:

สตริงภายในจะช่วยเพิ่มความเร็วในการเปรียบเทียบสตริงซึ่งบางครั้งก็เป็นปัญหาคอขวดในการใช้งาน (เช่นคอมไพเลอร์และภาษารันไทม์การเขียนโปรแกรมภาษาแบบไดนามิก) ที่พึ่งพาตารางแฮชอย่างมากด้วยคีย์สตริง การตรวจสอบว่าสายอักขระที่แตกต่างกันสองสายเท่ากันจะเกี่ยวข้องกับการตรวจสอบอักขระทุกตัวของสตริงทั้งสอง สิ่งนี้ช้าลงด้วยเหตุผลหลายประการ: มันเป็นโดยธรรมชาติ O (n) ในความยาวของสตริง; โดยทั่วไปจะต้องอ่านจากหน่วยความจำหลายภูมิภาคซึ่งใช้เวลา; และการอ่านแคชหน่วยประมวลผลเติมหมายถึงมีแคชน้อยกว่าสำหรับความต้องการอื่น ๆ ด้วยสตริงที่อยู่ภายในการทดสอบตัวตนของวัตถุอย่างง่ายจะเพียงพอหลังจากการดำเนินการฝึกหัดดั้งเดิม สิ่งนี้มักจะนำมาใช้เป็นการทดสอบความเท่าเทียมกันของตัวชี้

ดังนั้นเมื่อคุณมีตัวอักษรสตริงสองตัว (คำที่พิมพ์ลงในซอร์สโค้ดโปรแกรมของคุณล้อมรอบด้วยเครื่องหมายคำพูด) ในโปรแกรมของคุณที่มีค่าเท่ากันคอมไพเลอร์ Python จะกักสตริงโดยอัตโนมัติทำให้ทั้งคู่เก็บไว้ในที่เดียวกัน ตำแหน่งหน่วยความจำ (โปรดทราบว่าสิ่งนี้ไม่ได้เกิดขึ้นเสมอไปและกฎสำหรับเมื่อเกิดเหตุการณ์นี้ค่อนข้างซับซ้อนดังนั้นโปรดอย่าพึ่งพาพฤติกรรมนี้ในรหัสการผลิต!)

เนื่องจากในเซสชันการโต้ตอบของคุณทั้งสองสตริงจะถูกเก็บไว้จริงในตำแหน่งหน่วยความจำเดียวกันพวกเขาจึงมีตัวตนเดียวกันดังนั้นisผู้ประกอบการจึงทำงานได้ตามที่คาดไว้ แต่ถ้าคุณสร้างสตริงด้วยวิธีอื่น (แม้ว่าสตริงนั้นจะมีอักขระเหมือนกันทุกประการ) ดังนั้นสตริงนั้นอาจจะเท่ากันแต่ไม่ใช่สตริงเดียวกัน - นั่นคือมันมีเอกลักษณ์ที่แตกต่างกันเพราะเป็น เก็บไว้ในที่อื่นในหน่วยความจำ


6
ใครบ้างที่สามารถอ่านเพิ่มเติมเกี่ยวกับกฎที่ซับซ้อนได้เมื่อสตริงถูก interned
Noctis Skytower

88
+1 สำหรับคำอธิบายอย่างละเอียด ไม่แน่ใจว่าคำตอบอื่น ๆ ได้รับ upvotes มากมายโดยไม่อธิบายว่าเกิดอะไรขึ้นจริง
That1Guy

4
นี่คือสิ่งที่ฉันคิดเมื่อฉันอ่านคำถาม คำตอบที่ยอมรับนั้นสั้น แต่มีความจริง แต่คำตอบนี้อธิบายสิ่งต่าง ๆ ได้ดีกว่า ดี!
Sнаđошƒаӽ

3
@NoctisSkytower Googled เหมือนกันและพบguilload.com/python-string-interning
xtreak

5
@ naught101: ไม่กฎคือการเลือกระหว่าง==และisขึ้นอยู่กับชนิดของการตรวจสอบที่คุณต้องการ หากคุณสนใจเกี่ยวกับสตริงเป็นเท่ากับ (นั่นคือมีเนื้อหาเดียวกัน) ==แล้วคุณควรใช้ isหากคุณสนใจเกี่ยวกับว่ามีสองชื่อหลามหมายถึงวัตถุเช่นเดียวกันคุณควรใช้ คุณอาจต้องการisถ้าคุณเขียนโค้ดที่จัดการค่าต่าง ๆ มากมายโดยไม่สนใจเนื้อหาของพวกเขาหรืออื่น ๆ ถ้าคุณรู้ว่ามีบางอย่างเพียงอย่างเดียวและคุณต้องการละเว้นวัตถุอื่น ๆ ที่อ้างว่าเป็นสิ่งนั้น ==หากคุณไม่แน่ใจว่าเสมอเลือก
Daniel Pryden

108

isคำหลักคือการทดสอบตัวตนในขณะที่วัตถุ==คือการเปรียบเทียบค่า

หากคุณใช้isผลลัพธ์จะเป็นจริงถ้าหากวัตถุนั้นเป็นวัตถุเดียวกัน อย่างไรก็ตาม==จะเป็นจริงเมื่อใดก็ตามที่ค่าของวัตถุเหมือนกัน


57

สิ่งสุดท้ายที่ควรทราบคุณอาจใช้sys.internฟังก์ชันเพื่อให้แน่ใจว่าคุณได้รับการอ้างอิงถึงสตริงเดียวกัน:

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

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

โปรดทราบว่าinternฟังก์ชั่นที่เคยเป็น builtin ใน Python 2 แต่ถูกย้ายไปยังsysโมดูลใน Python 3


43

isคือการทดสอบตัวตน==คือการทดสอบความเท่าเทียมกัน สิ่งนี้หมายความว่าisเป็นวิธีตรวจสอบว่ามีสองสิ่งที่เหมือนกันหรือเทียบเท่ากัน

สมมติว่าคุณมีpersonวัตถุอย่างง่าย หากชื่อ 'แจ็ค' และ '23' ปีมันเทียบเท่ากับแจ็คอายุ 23 ปีอีกคน แต่ไม่ใช่คนเดียวกัน

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

พวกเขาอายุเท่ากัน แต่พวกเขาไม่ใช่คนเดียวกัน สตริงอาจเทียบเท่ากับสตริงอื่น แต่ไม่ใช่วัตถุเดียวกัน


ถ้าคุณเปลี่ยนการตั้งค่าที่จะไม่เปลี่ยนแปลงjack1.age = 99 นั่นเป็นเพราะพวกเขาเป็นสองกรณีแตกต่างกันดังนั้นjack2.age jack1 is not jack2อย่างไรก็ตามพวกเขาสามารถเท่าเทียมกันjack1 == jack2หากชื่อและอายุของพวกเขาเหมือนกัน มันซับซ้อนมากขึ้นสำหรับสตริงเนื่องจากสตริงนั้นไม่เปลี่ยนรูปใน Python และ Python มักจะนำอินสแตนซ์เดียวกันกลับมาใช้ซ้ำ ฉันชอบคำอธิบายนี้เพราะใช้กรณีธรรมดา (วัตถุปกติ) แทนที่จะเป็นกรณีพิเศษ (สตริง)
Flimm

37

นี่คือบันทึกย่อด้านข้าง แต่ในงูเหลือมสำนวนคุณมักจะเห็นสิ่งต่าง ๆ เช่น:

if x is None: 
    # some clauses

นี้มีความปลอดภัยเพราะมีการประกันเพื่อเป็นหนึ่งในตัวอย่างของวัตถุ Null (เช่นไม่มี)


1
เป็นเรื่องจริงจริงหรือเท็จ จะมีเพียงหนึ่งอินสแตนซ์เท่านั้นที่จะตรงกัน
HandyManDan

1
@HandmanManDan ใช่พวกเขาเป็นซิงเกิลตันทั้งใน python 2 และ 3
kamillitw

@kamillitw แต่ใน Python 2 คุณสามารถกำหนด False และ True ใหม่ได้
Martijn Pieters

28

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

ไม่เช่นนั้นคุณจะสงสัยว่าทำไมสิ่งต่าง ๆ ไม่ทำงานและทำไมสิ่งนี้จึงเกิดขึ้น:

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

ฉันไม่แน่ใจด้วยซ้ำว่าบางสิ่งบางอย่างรับประกันว่าจะเหมือนเดิมระหว่างการใช้งาน / การใช้งานของหลามที่แตกต่างกัน


1
ตัวอย่างที่น่าสนใจแสดงให้เห็นว่าการมอบหมาย int ทำให้ทริกเกอร์เงื่อนไขนี้เป็นอย่างไร ทำไมสิ่งนี้ถึงล้มเหลว มันเป็นเพราะการฝึกงานหรืออย่างอื่น?
พอล

ดูเหมือนว่าเหตุผลที่คืนเป็นเท็จอาจเกิดจากการใช้งานล่าม: stackoverflow.com/questions/132988/…
เปาโล


@ArchitJain ใช่ลิงก์เหล่านั้นอธิบายได้ค่อนข้างดี เมื่อคุณอ่านพวกเขาคุณจะรู้ว่าหมายเลขใดที่คุณสามารถใช้เป็น 'เปิด' ผมแค่หวังว่าพวกเขาจะอธิบายว่าทำไมมันยังคงไม่ได้เป็นความคิดที่ดีที่จะทำ :) คุณรู้ว่านี้ไม่ได้ทำให้มันเป็นความคิดที่ดีที่จะคิดคนอื่นไม่ดี (หรือว่าช่วงจำนวน internalized จะไม่เปลี่ยนแปลง)
แม็ทเทียค๊

20

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

นี่เป็นตัวอย่างที่ดี:

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

s1เป็นสตริง Unicode และs2เป็นสตริงปกติ พวกเขาไม่ใช่ประเภทเดียวกัน แต่เป็นค่าเดียวกัน


17

ฉันคิดว่ามันเกี่ยวข้องกับความจริงที่ว่าเมื่อการเปรียบเทียบ 'คือ' ประเมินว่าเป็นเท็จจะใช้วัตถุสองอย่างที่แตกต่างกัน หากประเมินเป็นจริงนั่นหมายความว่าภายในใช้วัตถุที่แน่นอนและไม่สร้างวัตถุใหม่อาจเป็นเพราะคุณสร้างขึ้นภายในเสี้ยววินาทีหรือประมาณวินาทีและเพราะไม่มีช่องว่างขนาดใหญ่ในระหว่างนั้น ใช้วัตถุเดียวกัน

นี่คือเหตุผลที่คุณควรใช้โอเปอเรเตอร์ความเสมอภาค==ไม่ใช่isเพื่อเปรียบเทียบค่าของวัตถุสตริง

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

ในตัวอย่างนี้ฉันทำ s2 ซึ่งเป็นวัตถุสตริงที่แตกต่างกันก่อนหน้านี้เท่ากับ 'หนึ่ง' แต่มันไม่ได้เป็นวัตถุเดียวกันsเพราะล่ามไม่ได้ใช้วัตถุเดียวกันเพราะฉันไม่ได้กำหนดให้ 'หนึ่ง' ถ้าฉันมีมันก็จะทำให้พวกเขาวัตถุเดียวกัน


3
การใช้.replace()เป็นตัวอย่างในบริบทนี้อาจไม่ใช่วิธีที่ดีที่สุดเนื่องจากความหมายของมันอาจสร้างความสับสน s2 = s2.replace()จะสร้างวัตถุสตริงใหม่เสมอกำหนดวัตถุสตริงใหม่ให้แล้วกำจัดวัตถุสตริงที่ใช้ในการชี้ไปที่ ดังนั้นแม้ว่าคุณจะได้รับวัตถุสตริงใหม่ก็ตาม s2s2s = s.replace('one', 'one')
Daniel Pryden

13

ฉันเชื่อว่านี่เป็นที่รู้จักในฐานะสตริง "ฝึกงาน" Python ทำสิ่งนี้เช่นเดียวกับ Java และทำ C และ C ++ เมื่อคอมไพล์ในโหมดที่ปรับให้เหมาะสม

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

ผลลัพธ์นี้ใน Python "คือ" โอเปอเรเตอร์คืน True เนื่องจากสองสตริงที่มีเนื้อหาเดียวกันชี้ไปที่วัตถุสตริงเดียวกัน สิ่งนี้จะเกิดขึ้นใน Java และ C

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


12

ฉันกำลังตอบคำถามแม้ว่าคำถามนั้นจะเก่าไปแล้วเพราะไม่มีคำตอบข้างต้นให้อ้างอิงการอ้างอิงภาษา

อันที่จริงผู้ประกอบการคือการตรวจสอบตัวตนและ == ผู้ประกอบการตรวจสอบความเท่าเทียมกัน

จากการอ้างอิงภาษา:

ประเภทส่งผลกระทบต่อพฤติกรรมวัตถุเกือบทุกด้าน แม้ความสำคัญของตัวตนของวัตถุที่ได้รับผลกระทบในความรู้สึกบางอย่างชนิดไม่เปลี่ยนรูปการดำเนินงานที่ค่าใหม่คำนวณอาจเป็นจริงกลับมีการอ้างอิงถึงวัตถุที่มีอยู่ใด ๆ กับประเภทเดียวกันและความคุ้มค่าในขณะที่สำหรับวัตถุที่ไม่แน่นอนนี้ไม่ได้รับอนุญาต เช่นหลังจาก a = 1; b = 1, a และ b อาจหรือไม่อาจอ้างถึงวัตถุเดียวกันกับค่าหนึ่งขึ้นอยู่กับการใช้งาน แต่หลังจาก c = []; d = [], c และ d รับประกันว่าจะอ้างถึงรายการว่างที่แตกต่างกันไม่ซ้ำกันสร้างขึ้นใหม่ (โปรดทราบว่า c = d = [] กำหนดวัตถุเดียวกันให้กับทั้ง c และ d)

ดังนั้นจากคำสั่งด้านบนเราสามารถอนุมานได้ว่าสตริงซึ่งเป็นประเภทไม่เปลี่ยนรูปอาจล้มเหลวเมื่อตรวจสอบด้วย "เป็น" และอาจตรวจสอบประสบความสำเร็จเมื่อตรวจสอบด้วย "เป็น"

เช่นเดียวกับ int, tuple ซึ่งเป็นประเภทที่ไม่เปลี่ยนรูป


8

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

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

ในตัวอย่างนี้ Python สร้างเพียงหนึ่งวัตถุสตริงและทั้งสองaและbอ้างถึงมัน เหตุผลก็คือ Python ใช้แคชภายในและนำสตริงบางส่วนกลับมาใช้เป็นการปรับให้เหมาะสมมีเพียงสตริง 'Banana' ในหน่วยความจำที่ใช้ร่วมกันโดย a และ b; ในการทริกเกอร์การทำงานปกติคุณต้องใช้สตริงที่ยาวกว่า:

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

เมื่อคุณสร้างสองรายการคุณจะได้รับวัตถุสองรายการ:

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

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

หากaอ้างถึงวัตถุและคุณกำหนดให้b = aตัวแปรทั้งสองอ้างถึงวัตถุเดียวกัน:

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

7

isจะเปรียบเทียบตำแหน่งหน่วยความจำ มันถูกใช้สำหรับการเปรียบเทียบระดับวัตถุ

==จะเปรียบเทียบตัวแปรในโปรแกรม มันถูกใช้สำหรับการตรวจสอบที่ระดับค่า

is ตรวจสอบการเทียบเท่าระดับที่อยู่

== ตรวจสอบความเท่าเทียมกันระดับค่า


3

isคือการทดสอบตัวตนคือการทดสอบ==ความเท่าเทียมกัน (ดูPython Documentation )

ในกรณีส่วนใหญ่ถ้าแล้วa is b a == bแต่มีข้อยกเว้นเช่น:

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

ดังนั้นคุณสามารถใช้isสำหรับการทดสอบตัวตนเท่านั้นไม่เคยทดสอบความเท่าเทียมกัน

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