Mengapa membandingkan string menggunakan '==' atau 'is' terkadang menghasilkan hasil yang berbeda?

1147

Saya punya program Python di mana dua variabel diatur ke nilai 'public'. Dalam ekspresi bersyarat saya memiliki perbandingan var1 is var2yang gagal, tetapi jika saya mengubahnya var1 == var2kembali True.

Sekarang jika saya membuka interpreter Python saya dan melakukan perbandingan "is" yang sama, itu berhasil.

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

Apa yang kulewatkan di sini?

jottos
sumber
8
lihat: stackoverflow.com/questions/1392433/...
Nick Dandoulakis
3
Masalah ini juga terjadi ketika Anda membaca konsol masukan melalui misalnya: input = raw_input("Decide (y/n): "). Dalam hal ini input "y" dan if input == 'y':akan mengembalikan "True" sementara if input is 'y':akan mengembalikan False.
Semjon Mössinger
4
Blog ini memberikan penjelasan yang jauh lebih lengkap daripada jawaban apa pun guilload.com/python-string-interning
Chris_Rands
1
Seperti @ chris-rico menyebutkan, saya penjelasan bagus di sini stackoverflow.com/q/15541404/1695680
ThorSummoner

Jawaban:

1533

isadalah pengujian identitas, ==adalah pengujian kesetaraan. apa yang terjadi dalam kode Anda akan ditiru dalam bahasa juru bahasa seperti ini:

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

jadi, tidak heran mereka tidak sama, kan?

Dengan kata lain: isis theid(a) == id(b)

SilentGhost
sumber
17
ahh sama dengan eq? vs sama? dalam skema, mengerti.
jottos
47
Atau ==vs .equals()di Jawa. Bagian terbaiknya adalah bahwa Python ==tidak analog dengan Java ==.
MatrixFrog
11
@ Крайст: hanya ada satu Nonenilai. Jadi selalu memiliki id yang sama.
SilentGhost
18
Ini tidak membahas contoh OP "is -> True".
user2864740
6
@AlexanderSupertramp, karena string interning .
Chris Rico
570

Jawaban lain di sini benar: isdigunakan untuk perbandingan identitas , sedangkan ==digunakan untuk perbandingan kesetaraan . Karena yang Anda pedulikan adalah kesetaraan (dua string harus berisi karakter yang sama), dalam hal ini isoperator itu salah dan Anda harus menggunakan ==sebagai gantinya.

Alasannya isbekerja secara interaktif adalah bahwa (sebagian besar) string literal diinternir secara default. Dari Wikipedia:

String Interned mempercepat perbandingan string, yang kadang-kadang merupakan hambatan kinerja dalam aplikasi (seperti kompiler dan runtime bahasa pemrograman dinamis) yang sangat bergantung pada tabel hash dengan kunci string. Tanpa magang, memeriksa bahwa dua string yang berbeda sama melibatkan memeriksa setiap karakter dari kedua string. Ini lambat karena beberapa alasan: itu inheren O (n) di panjang string; biasanya memerlukan bacaan dari beberapa wilayah memori, yang membutuhkan waktu; dan bacaan mengisi cache prosesor, yang berarti ada lebih sedikit cache yang tersedia untuk kebutuhan lain. Dengan string yang diinternir, tes identitas objek sederhana sudah cukup setelah operasi intern asli; ini biasanya diimplementasikan sebagai uji kesetaraan pointer,

Jadi, ketika Anda memiliki dua string literal (kata-kata yang secara harfiah diketik ke dalam kode sumber program Anda, dikelilingi oleh tanda kutip) dalam program Anda yang memiliki nilai yang sama, kompiler Python akan secara otomatis menginternasionalkan string, membuat keduanya disimpan pada saat yang sama lokasi memori. (Perhatikan bahwa ini tidak selalu terjadi, dan aturan untuk kapan ini terjadi cukup berbelit-belit, jadi tolong jangan mengandalkan perilaku ini dalam kode produksi!)

Karena dalam sesi interaktif Anda, kedua string sebenarnya disimpan di lokasi memori yang sama, mereka memiliki identitas yang sama , sehingga isoperator bekerja seperti yang diharapkan. Tetapi jika Anda membangun sebuah string dengan metode lain (bahkan jika string itu mengandung karakter yang persis sama), maka string tersebut mungkin sama , tetapi itu bukan string yang sama - yaitu, ia memiliki identitas yang berbeda , karena itu adalah disimpan di tempat yang berbeda di memori.

Daniel Pryden
sumber
6
Di mana seseorang dapat membaca lebih lanjut tentang aturan berbelit-belit ketika string diinternir?
Noctis Skytower
89
+1 untuk penjelasan menyeluruh. Tidak yakin bagaimana jawaban yang lain menerima begitu banyak upvotes tanpa menjelaskan apa yang SEBENARNYA terjadi.
That1Guy
4
inilah yang saya pikirkan ketika saya membaca pertanyaan itu. Jawaban yang diterima pendek namun mengandung fakta, tetapi jawaban ini menjelaskan hal-hal yang jauh lebih baik. Bagus!
Sнаđошƒаӽ
3
@NoctisSkytower Googled sama dan menemukan ini guilload.com/python-string-interning
xtreak
5
@ naught101: Tidak, aturannya adalah memilih ==dan isberdasarkan jenis cek yang Anda inginkan. Jika Anda peduli tentang string yang sama (yaitu, memiliki konten yang sama) maka Anda harus selalu menggunakannya ==. Jika Anda peduli apakah ada dua nama Python yang merujuk ke instance objek yang sama, Anda harus menggunakan is. Anda mungkin perlu isjika Anda menulis kode yang menangani banyak nilai berbeda tanpa memperhatikan isinya, atau jika Anda tahu hanya ada satu dari sesuatu dan Anda ingin mengabaikan objek lain yang berpura-pura menjadi benda itu. Jika Anda tidak yakin, selalu pilih ==.
Daniel Pryden
108

Kata iskunci adalah tes untuk identitas objek sedangkan ==perbandingan nilai.

Jika Anda menggunakan is, hasilnya akan benar jika dan hanya jika objek tersebut adalah objek yang sama. Namun, ==akan benar setiap saat nilai objek sama.

Thomas Owens
sumber
57

Satu hal terakhir yang perlu diperhatikan, Anda dapat menggunakan sys.internfungsi untuk memastikan bahwa Anda mendapatkan referensi ke string yang sama:

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

Seperti yang ditunjukkan di atas, Anda tidak boleh menggunakan isuntuk menentukan kesetaraan string. Tapi ini mungkin berguna untuk mengetahui jika Anda memiliki beberapa persyaratan aneh untuk digunakan is.

Perhatikan bahwa internfungsi yang digunakan adalah builtin pada Python 2 tetapi dipindahkan ke sysmodul dengan Python 3.

Jason Baker
sumber
43

isadalah pengujian identitas, ==adalah pengujian kesetaraan. Ini artinya adalah iscara untuk memeriksa apakah dua hal adalah hal yang sama , atau hanya setara.

Katakanlah Anda punya personobjek sederhana . Jika diberi nama 'Jack' dan '23' tahun, itu setara dengan Jack lain yang berusia 23 tahun, tetapi itu bukan orang yang sama.

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

Mereka seusia, tapi mereka bukan orang yang sama. Sebuah string mungkin setara dengan yang lain, tetapi itu bukan objek yang sama.

TankorSmash
sumber
Jika Anda mengubah set jack1.age = 99, itu tidak akan berubah jack2.age. Itu karena mereka adalah dua contoh yang berbeda, jadi jack1 is not jack2. Namun, mereka dapat sama satu sama lain jack1 == jack2jika nama dan umur mereka sama. Itu menjadi lebih rumit untuk string, karena string tidak dapat diubah dalam Python, dan Python sering menggunakan kembali contoh yang sama. Saya suka penjelasan ini karena menggunakan case sederhana (objek normal) daripada case khusus (string).
Flimm
37

Ini adalah catatan tambahan, tetapi dengan python idiomatik, Anda akan sering melihat hal-hal seperti:

if x is None: 
    # some clauses

Ini aman, karena dijamin ada satu instance dari Objek Null (yaitu, Tidak ada) .

Gregg Lind
sumber
1
Apakah hal yang sama berlaku untuk Benar dan Salah? Hanya satu contoh jadi apakah akan cocok?
HandyManDan
1
@HandyManDan Ya, mereka adalah lajang di python 2 dan 3.
kamillitw
@kamillitw tetapi dengan Python 2 Anda dapat menetapkan ulang False dan True.
Martijn Pieters
28

Jika Anda tidak yakin apa yang Anda lakukan, gunakan '=='. Jika Anda memiliki sedikit pengetahuan tentangnya, Anda dapat menggunakan 'is' untuk objek yang dikenal seperti 'None'.

Kalau tidak, Anda akan bertanya-tanya mengapa hal-hal tidak bekerja dan mengapa ini terjadi:

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

Saya bahkan tidak yakin apakah beberapa hal dijamin tetap sama antara versi / implementasi python yang berbeda.

Mattias Nilsson
sumber
1
Contoh menarik yang menunjukkan bagaimana menetapkan ulang int membuat pemicu kondisi ini. Mengapa ini gagal? Apakah karena magang atau sesuatu yang lain?
Paul
Sepertinya alasan is return false mungkin disebabkan oleh implementasi interpreter: stackoverflow.com/questions/132988/…
Paul
@ArchitJain Ya, tautan itu menjelaskannya dengan cukup baik. Saat Anda membacanya, Anda akan tahu nomor apa yang dapat Anda gunakan 'aktif'. Saya hanya berharap mereka akan menjelaskan mengapa itu masih bukan ide yang baik untuk melakukan itu :) Anda tahu ini tidak membuatnya menjadi ide yang baik untuk mengasumsikan semua orang juga melakukannya (atau bahwa kisaran nomor yang diinternalisasi tidak akan pernah berubah)
Mattias Nilsson
20

Dari pengalaman saya yang terbatas dengan python, isdigunakan untuk membandingkan dua objek untuk melihat apakah mereka adalah objek yang sama dengan dua objek berbeda dengan nilai yang sama. ==digunakan untuk menentukan apakah nilainya identik.

Ini adalah contoh yang bagus:

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

s1adalah string unicode, dan s2merupakan string normal. Mereka bukan tipe yang sama, tetapi memiliki nilai yang sama.

Jack M.
sumber
17

Saya pikir itu ada hubungannya dengan fakta bahwa, ketika perbandingan 'adalah' dievaluasi menjadi false, dua objek berbeda digunakan. Jika bernilai true, itu berarti secara internal ia menggunakan objek yang sama persis dan tidak membuat yang baru, mungkin karena Anda membuatnya dalam fraksi 2 atau lebih detik dan karena tidak ada kesenjangan waktu yang besar di antara dioptimalkan dan menggunakan objek yang sama.

Inilah sebabnya mengapa Anda harus menggunakan operator persamaan ==, bukan is, untuk membandingkan nilai objek string.

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

Dalam contoh ini, saya membuat s2, yang merupakan objek string berbeda yang sebelumnya sama dengan 'satu' tetapi bukan objek yang sama dengan s, karena penerjemah tidak menggunakan objek yang sama karena saya awalnya tidak menetapkannya ke 'satu', jika saya memilikinya akan membuat mereka menjadi objek yang sama.

meder omuraliev
sumber
3
Menggunakan .replace()sebagai contoh dalam konteks ini mungkin bukan yang terbaik, karena semantiknya dapat membingungkan. s2 = s2.replace()akan selalu membuat objek string baru , menetapkan objek string baru s2, dan kemudian membuang objek string yang s2digunakan untuk menunjuk. Jadi, bahkan jika Anda melakukannya, s = s.replace('one', 'one')Anda masih akan mendapatkan objek string baru.
Daniel Pryden
13

Saya percaya bahwa ini dikenal sebagai string "diinternir". Python melakukan ini, begitu pula Java, dan begitu juga C dan C ++ ketika mengkompilasi dalam mode yang dioptimalkan.

Jika Anda menggunakan dua string yang identik, alih-alih membuang-buang memori dengan membuat dua objek string, semua string yang diinternir dengan konten yang sama mengarah ke memori yang sama.

Ini menghasilkan operator "is" Python yang mengembalikan True karena dua string dengan konten yang sama menunjuk ke objek string yang sama. Ini juga akan terjadi di Jawa dan C.

Ini hanya berguna untuk penghematan memori. Anda tidak dapat mengandalkannya untuk menguji kesetaraan string, karena berbagai penterjemah dan kompiler dan mesin JIT tidak selalu dapat melakukannya.

Zan Lynx
sumber
12

Saya menjawab pertanyaan meskipun pertanyaannya sudah lama karena tidak ada jawaban di atas yang mengutip referensi bahasa

Sebenarnya operator memeriksa identitas dan == operator memeriksa kesetaraan,

Dari Referensi Bahasa:

Jenis mempengaruhi hampir semua aspek perilaku objek. Bahkan pentingnya identitas objek dipengaruhi dalam beberapa hal: untuk tipe yang tidak dapat diubah, operasi yang menghitung nilai-nilai baru sebenarnya dapat mengembalikan referensi ke objek yang ada dengan tipe dan nilai yang sama, sedangkan untuk objek yang dapat berubah ini tidak diperbolehkan . Misalnya, setelah a = 1; b = 1, a dan b mungkin atau mungkin tidak merujuk ke objek yang sama dengan nilai satu, tergantung pada implementasinya, tetapi setelah c = []; d = [], c dan d dijamin untuk merujuk ke dua daftar kosong yang berbeda, unik, yang baru dibuat. (Perhatikan bahwa c = d = [] menetapkan objek yang sama untuk c dan d.)

jadi dari pernyataan di atas kita dapat menyimpulkan bahwa string yang merupakan tipe tidak berubah mungkin gagal ketika diperiksa dengan "is" dan dapat diperiksa berhasil ketika diperiksa dengan "is"

Hal yang sama berlaku untuk int, tuple yang juga merupakan tipe yang tidak berubah

Ram
sumber
8

The ==nilai tes Operator kesetaraan. The isidentitas tes Operator objek, tes Python apakah kedua benar-benar objek yang sama (yaitu, hidup di alamat yang sama di memori).

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

Dalam contoh ini, Python hanya membuat satu objek string, dan keduanya adan bmerujuknya. Alasannya adalah bahwa Python secara internal menyimpan dan menggunakan kembali beberapa string sebagai optimisasi, hanya ada string 'pisang' dalam memori, dibagi oleh a dan b; Untuk memicu perilaku normal, Anda perlu menggunakan string yang lebih panjang:

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

Saat Anda membuat dua daftar, Anda mendapatkan dua objek:

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

Dalam hal ini kita dapat mengatakan bahwa kedua daftar itu setara, karena mereka memiliki elemen yang sama, tetapi tidak identik, karena mereka bukan objek yang sama. Jika dua objek identik, mereka juga setara, tetapi jika mereka setara, mereka tidak harus sama.

Jika amerujuk ke objek dan Anda tetapkan b = a, maka kedua variabel merujuk ke objek yang sama:

>>> a = [1, 2, 3]
>>> b = a
>>> b is a
True
X. Wang
sumber
7

isakan membandingkan lokasi memori. Ini digunakan untuk perbandingan tingkat objek.

==akan membandingkan variabel dalam program. Ini digunakan untuk memeriksa pada level nilai.

is memeriksa kesetaraan tingkat alamat

== memeriksa kesetaraan tingkat nilai

johnashu
sumber
3

isadalah pengujian identitas, ==adalah pengujian kesetaraan (lihat Dokumentasi Python ).

Dalam kebanyakan kasus, jika a is b, maka a == b. Tetapi ada pengecualian, misalnya:

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

Jadi, Anda hanya dapat menggunakan isuntuk tes identitas, tidak pernah tes kesetaraan.

Ryan
sumber