Python! = Operasi vs "tidak"

250

Dalam komentar pada pertanyaan ini , saya melihat pernyataan yang merekomendasikan penggunaan

result is not None

vs.

result != None

Saya bertanya-tanya apa perbedaannya, dan mengapa yang satu lebih direkomendasikan daripada yang lain?

viksit
sumber
1
Hmm. Sementara jawaban untuk kedua pertanyaan adalah konsep yang sama, saya pikir jawaban positif dan terperinci di sini berkontribusi secara independen terhadap konsep pengujian identitas dan kesetaraan.
viksit

Jawaban:

301

==adalah ujian kesetaraan . Ia memeriksa apakah sisi kanan dan sisi kiri adalah benda yang sama (sesuai dengan __eq__atau__cmp__ metode.)

isadalah tes identitas . Ia memeriksa apakah sisi kanan dan sisi kiri adalah objek yang sama. Tidak ada panggilan metode yang dilakukan, objek tidak dapat memengaruhiis operasi.

Anda menggunakan is(dan is not) untuk lajang, seperti None, di mana Anda tidak peduli dengan objek yang mungkin ingin berpura-pura Noneatau di mana Anda ingin melindungi terhadap benda yang pecah ketika dibandingkan dengan None.

Thomas Wouters
sumber
3
Terima kasih atas jawabannya - dapatkah Anda menguraikan situasi ketika sebuah objek dapat pecah, dibandingkan dengan Tidak Ada?
viksit
3
@viksit. Nonememiliki beberapa metode dan hampir tidak memiliki atribut. Jika __eq__pengujian Anda mengharapkan suatu metode atau atribut, itu mungkin pecah. def __eq__( self, other ): return self.size == other.size. Misalnya, akan pecah jika otherkebetulan None.
S.Lott
36
Cara favorit saya untuk memahami ini adalah: Python isseperti Java ==. Python ==adalah seperti Jawa .equals(). Tentu saja ini hanya membantu jika Anda tahu Java.
MatrixFrog
4
@ MatrixFrog: Dalam PHP atau JavaScript kita akan mengatakan itu isseperti ===(sangat sama), dan sebaliknya is notseperti !==(tidak persis sama).
Orwellophile
3
Apakah is notoperator tunggal atau hanya meniadakan hasil isseperti internal not foo is bar?
Asad Moosvi
150

Pertama, biarkan saya membahas beberapa istilah. Jika Anda hanya ingin pertanyaan Anda dijawab, gulir ke bawah ke "Menjawab pertanyaan Anda".

Definisi

Identitas objek : Saat Anda membuat objek, Anda dapat menetapkannya ke variabel. Anda kemudian dapat juga menugaskannya ke variabel lain. Dan satu lagi.

>>> button = Button()
>>> cancel = button
>>> close = button
>>> dismiss = button
>>> print(cancel is close)
True

Dalam hal ini, cancel, close, dan dismisssemua mengacu pada objek yang sama dalam memori. Anda hanya membuat satu Buttonobjek, dan ketiga variabel merujuk ke objek yang satu ini. Kami mengatakan bahwa cancel,, closedan dismisssemua mengacu pada objek yang identik ; yaitu, mereka merujuk ke satu objek tunggal.

Kesetaraan objek : Ketika Anda membandingkan dua objek, Anda biasanya tidak peduli bahwa itu merujuk ke objek yang sama persis di memori. Dengan persamaan objek, Anda dapat menentukan aturan Anda sendiri untuk membandingkan dua objek. Ketika Anda menulis if a == b:, Anda pada dasarnya mengatakan if a.__eq__(b):. Ini memungkinkan Anda menentukan __eq__metode aagar Anda dapat menggunakan logika perbandingan sendiri.

Dasar pemikiran untuk perbandingan kesetaraan

Dasar Pemikiran: Dua objek memiliki data yang sama persis, tetapi tidak identik. (Mereka bukan objek yang sama dalam memori.) Contoh: String

>>> greeting = "It's a beautiful day in the neighbourhood."
>>> a = unicode(greeting)
>>> b = unicode(greeting)
>>> a is b
False
>>> a == b
True

Catatan: Saya menggunakan string unicode di sini karena Python cukup pintar untuk menggunakan kembali string biasa tanpa membuat yang baru di memori.

Di sini, saya memiliki dua string unicode, adan b. Mereka memiliki konten yang sama persis, tetapi mereka bukan objek yang sama dalam memori. Namun, ketika kami membandingkannya, kami ingin mereka membandingkan yang sama. Apa yang terjadi di sini adalah bahwa objek unicode telah mengimplementasikan __eq__metode ini.

class unicode(object):
    # ...

    def __eq__(self, other):
        if len(self) != len(other):
            return False

        for i, j in zip(self, other):
            if i != j:
                return False

        return True

Catatan: __eq__aktif unicodepasti diterapkan lebih efisien daripada ini.

Dasar Pemikiran: Dua objek memiliki data yang berbeda, tetapi dianggap objek yang sama jika beberapa data utama adalah sama. Contoh: Sebagian besar tipe data model

>>> import datetime
>>> a = Monitor()
>>> a.make = "Dell"
>>> a.model = "E770s"
>>> a.owner = "Bob Jones"
>>> a.warranty_expiration = datetime.date(2030, 12, 31)
>>> b = Monitor()
>>> b.make = "Dell"
>>> b.model = "E770s"
>>> b.owner = "Sam Johnson"
>>> b.warranty_expiration = datetime.date(2005, 8, 22)
>>> a is b
False
>>> a == b
True

Di sini, saya punya dua monitor Dell, adan b. Mereka memiliki merek dan model yang sama. Namun, mereka tidak memiliki data yang sama atau objek yang sama dalam memori. Namun, ketika kami membandingkannya, kami ingin mereka membandingkan yang sama. Apa yang terjadi di sini adalah objek Monitor mengimplementasikan __eq__metode.

class Monitor(object):
    # ...

    def __eq__(self, other):
        return self.make == other.make and self.model == other.model

Menjawab pertanyaan Anda

Saat membandingkan None, selalu gunakan is not. Tidak ada yang tunggal dalam Python - hanya ada satu contoh dalam memori.

Dengan membandingkan identitas , ini dapat dilakukan dengan sangat cepat. Python memeriksa apakah objek yang Anda maksud memiliki alamat memori yang sama dengan objek global None - perbandingan dua angka yang sangat, sangat cepat.

Dengan membandingkan kesetaraan , Python harus mencari apakah objek Anda memiliki __eq__metode. Jika tidak, itu akan memeriksa setiap superclass mencari __eq__metode. Jika menemukan satu, Python menyebutnya. Ini sangat buruk jika __eq__metode ini lambat dan tidak segera kembali ketika melihat bahwa objek lainnyaNone .

Apakah Anda tidak menerapkannya __eq__? Kemudian Python mungkin akan menemukan __eq__metode objectdan menggunakannya sebagai gantinya - yang hanya memeriksa identitas objek saja.

Saat membandingkan sebagian besar hal lain dengan Python, Anda akan menggunakannya !=.

Wesley
sumber
42

Pertimbangkan yang berikut ini:

class Bad(object):
    def __eq__(self, other):
        return True

c = Bad()
c is None # False, equivalent to id(c) == id(None)
c == None # True, equivalent to c.__eq__(None)
Alok Singhal
sumber
1
Ini adalah contoh yang sangat membantu dan sederhana. Terima kasih.
msarafzadeh
18

Noneadalah singleton, oleh karena itu perbandingan identitas akan selalu berfungsi, sedangkan objek dapat memalsukan perbandingan kesetaraan melalui .__eq__().

Ignacio Vazquez-Abrams
sumber
Ah menarik! Dalam situasi apa seseorang ingin memalsukan perbandingan kesetaraan btw? Saya menduga ini memiliki implikasi keamanan dalam beberapa cara.
viksit
1
Ini bukan tentang memalsukan kesetaraan, ini tentang menerapkan kesetaraan. Ada banyak alasan untuk ingin mendefinisikan bagaimana suatu objek dibandingkan dengan yang lain.
Thomas Wouters
1
Saya akan mengatakan itu lebih banyak implikasi kebingungan daripada implikasi keamanan.
Greg Hewgill
2
Saya belum mengajukan alasan untuk memalsukan kesetaraan None, tetapi perilaku yang salah tentang hal itu Nonedapat terjadi sebagai efek samping dari penerapan kesetaraan terhadap jenis lainnya. Ini bukan implikasi keamanan, tetapi implikasi kebenarannya.
Ignacio Vazquez-Abrams
Ah begitu, saya mengerti. Terima kasih untuk klarifikasi.
viksit
10
>>> () adalah ()
Benar
>>> 1 adalah 1
Benar
>>> (1,) == (1,)
Benar
>>> (1,) adalah (1,)
Salah
>>> a = (1,)
>>> b = a
>>> a adalah b
Benar

Beberapa objek adalah lajang, dan karenanya issama dengan ==. Sebagian besar tidak.

singkat
sumber
4
Sebagian besar ini hanya bekerja secara kebetulan / detail implementasi. ()dan 1secara inheren bukan lajang.
Mike Graham
1
Dalam implementasi CPython, integer kecil ( -NSMALLNEGINTS <= n <= NSMALLPOSINTS) dan tuple kosong adalah lajang. Memang tidak didokumentasikan atau dijamin, tetapi tidak mungkin berubah.
ephemient
3
Begitulah penerapannya, tetapi tidak bermakna atau bermanfaat atau mendidik.
Mike Graham
1
Dan khususnya, CPython bukan satu-satunya implementasi Python. Mengandalkan perilaku yang dapat bervariasi di seluruh implementasi Python tampaknya umumnya menjadi Ide Buruk ™ bagi saya.
me_and