Mengapa "jika tidak someobj:" lebih baik daripada "jika someobj == Tidak ada:" dengan Python?

127

Saya telah melihat beberapa contoh kode seperti ini:

if not someobj:
    #do something

Tapi saya bertanya-tanya mengapa tidak melakukan:

if someobj == None:
    #do something

Apakah ada perbedaan? Apakah yang satu memiliki keunggulan dibandingkan yang lain?

fuentesjr
sumber
13
Umumnya 'someobj is None' lebih disukai daripada 'someobj == None'
Aaron Maenpaa
bagaimana dengan pseudocode jika X tidak ada? Atau X != None?
Charlie Parker
pertanyaan ini lebih jelas daripada yang lain ... pujian ...
alamin

Jawaban:

187

Dalam tes pertama, Python mencoba untuk mengkonversi objek ke boolnilai jika belum. Secara kasar, kami menanyakan objek: apakah Anda bermakna atau tidak? Ini dilakukan dengan menggunakan algoritma berikut:

  1. Jika objek memiliki __nonzero__metode khusus (seperti halnya numerik built-in, intdan float), ia memanggil metode ini. Itu harus mengembalikan boolnilai yang kemudian langsung digunakan, atau intnilai yang dianggap Falsejika sama dengan nol.

  2. Jika tidak, jika objek memiliki __len__metode khusus (seperti halnya wadah built-in, list, dict, set, tuple, ...), itu panggilan metode ini, mengingat wadah Falsejika kosong (panjang adalah nol).

  3. Jika tidak, objek dianggap Truekecuali kecuali Nonedalam hal ini, itu dianggap False.

Dalam tes kedua, objek dibandingkan untuk kesetaraan None. Di sini, kita bertanya objek, "Apakah Anda sama dengan nilai lainnya ini?" Ini dilakukan dengan menggunakan algoritma berikut:

  1. Jika objek memiliki __eq__metode, itu disebut, dan nilai kembali kemudian dikonversi ke boolnilai dan digunakan untuk menentukan hasil dari if.

  2. Kalau tidak, jika objek memiliki __cmp__metode, itu disebut. Fungsi ini harus mengembalikan inturutan kedua objek yang ditunjukkan ( -1jika self < other, 0jika self == other, +1jika self > other).

  3. Jika tidak, objek tersebut dibandingkan untuk identitas (mis. Mereka merujuk ke objek yang sama, seperti yang dapat diuji oleh isoperator).

Ada tes lain yang mungkin menggunakan isoperator. Kami akan bertanya objek, "Apakah Anda objek khusus ini?"

Secara umum, saya akan merekomendasikan untuk menggunakan tes pertama dengan nilai-nilai non-numerik, untuk menggunakan tes untuk kesetaraan ketika Anda ingin membandingkan objek yang sifatnya sama (dua string, dua angka, ...) dan untuk memeriksa identitas hanya ketika menggunakan nilai sentinel ( Noneartinya tidak diinisialisasi untuk bidang anggota sebagai contoh, atau saat menggunakan getattratau __getitem__metode).

Untuk meringkas, kami memiliki:

>>> class A(object):
...    def __repr__(self):
...        return 'A()'
...    def __nonzero__(self):
...        return False

>>> class B(object):
...    def __repr__(self):
...        return 'B()'
...    def __len__(self):
...        return 0

>>> class C(object):
...    def __repr__(self):
...        return 'C()'
...    def __cmp__(self, other):
...        return 0

>>> class D(object):
...    def __repr__(self):
...        return 'D()'
...    def __eq__(self, other):
...        return True

>>> for obj in ['', (), [], {}, 0, 0., A(), B(), C(), D(), None]:
...     print '%4s: bool(obj) -> %5s, obj == None -> %5s, obj is None -> %5s' % \
...         (repr(obj), bool(obj), obj == None, obj is None)
  '': bool(obj) -> False, obj == None -> False, obj is None -> False
  (): bool(obj) -> False, obj == None -> False, obj is None -> False
  []: bool(obj) -> False, obj == None -> False, obj is None -> False
  {}: bool(obj) -> False, obj == None -> False, obj is None -> False
   0: bool(obj) -> False, obj == None -> False, obj is None -> False
 0.0: bool(obj) -> False, obj == None -> False, obj is None -> False
 A(): bool(obj) -> False, obj == None -> False, obj is None -> False
 B(): bool(obj) -> False, obj == None -> False, obj is None -> False
 C(): bool(obj) ->  True, obj == None ->  True, obj is None -> False
 D(): bool(obj) ->  True, obj == None ->  True, obj is None -> False
None: bool(obj) -> False, obj == None ->  True, obj is None ->  True
Sylvain Defresne
sumber
4
Meskipun secara teknis benar, ini tidak menjelaskan bahwa tupel, daftar, dicts, strs, unicodes, ints, float, dll memiliki nol . Jauh lebih umum untuk mengandalkan nilai kebenaran dari tipe bawaan daripada mengandalkan metode bukan-nol khusus .
ddaa
50

Ini sebenarnya adalah praktik yang buruk. Sekali waktu, itu dianggap OK untuk dengan santai memperlakukan None dan False sebagai serupa. Namun, karena Python 2.2 ini bukan kebijakan terbaik.

Pertama, ketika Anda melakukan tes if xatau if not xsemacamnya, Python harus secara implisit dikonversi xke boolean. Aturan untuk boolfungsi menggambarkan rakit hal-hal yang Salah; yang lainnya Benar. Jika nilai x tidak benar untuk memulai, konversi implisit ini sebenarnya bukan cara yang paling jelas untuk mengatakan sesuatu.

Sebelum Python 2.2, tidak ada fungsi bool, jadi itu bahkan kurang jelas.

Kedua, Anda seharusnya tidak benar-benar mengujinya == None. Anda harus menggunakan is Nonedan is not None.

Lihat PEP 8, Panduan Gaya untuk Kode Python .

- Comparisons to singletons like None should always be done with
  'is' or 'is not', never the equality operators.

  Also, beware of writing "if x" when you really mean "if x is not None"
  -- e.g. when testing whether a variable or argument that defaults to
  None was set to some other value.  The other value might have a type
  (such as a container) that could be false in a boolean context!

Ada berapa lajang? Lima: None, True, False, NotImplementeddan Ellipsis. Karena Anda benar-benar tidak mungkin untuk menggunakan NotImplementedatau Ellipsis, dan Anda tidak akan pernah mengatakannya if x is True(karena if xjauh lebih jelas), Anda hanya akan pernah menguji None.

S.Lott
sumber
3
Bentuk kedua secara kategoris bukan praktik yang buruk. PEP 8 merekomendasikan penggunaan jika x dua kali . Pertama untuk urutan (bukan menggunakan len) dan kemudian untuk Benar dan Salah (bukan menggunakan adalah). Praktis semua kode Python yang pernah saya lihat menggunakan x dan jika tidak x.
Antti Rasinen
35

Karena Nonebukan satu-satunya hal yang dianggap salah.

if not False:
    print "False is false."
if not 0:
    print "0 is false."
if not []:
    print "An empty list is false."
if not ():
    print "An empty tuple is false."
if not {}:
    print "An empty dict is false."
if not "":
    print "An empty string is false."

False, 0, (), [], {}Dan ""semua berbeda dari None, sehingga Anda dua potongan kode yang tidak setara.

Selain itu, pertimbangkan hal berikut:

>>> False == 0
True
>>> False == ()
False

if object:adalah tidak cek kesetaraan. 0, (), [], None, {}, Dll yang semua berbeda satu sama lain, tetapi mereka semua mengevaluasi ke False.

Ini adalah "keajaiban" di balik ekspresi hubungan arus pendek seperti:

foo = bar and spam or eggs

yang merupakan singkatan untuk:

if bar:
    foo = spam
else:
    foo = eggs

walaupun Anda benar-benar harus menulis:

foo = spam if bar else egg
badp
sumber
Mengenai pertanyaan terakhir Anda, mereka setara.
Sylvain Defresne
Dan keduanya salah karena "" salah. Yang kedua harus membaca '("",) atau ("s",)'. Bagaimanapun, versi modern python memiliki operator ternary yang tepat. Retasan rawan kesalahan ini harus dibuang.
ddaa
4

PEP 8 - Style Guide untuk Kode Python merekomendasikan penggunaan adalah atau tidak jika Anda menguji untuk Tidak-ness

- Comparisons to singletons like None should always be done with
  'is' or 'is not', never the equality operators.

Di sisi lain jika Anda menguji lebih dari ketiadaan, Anda harus menggunakan operator boolean.

Vinko Vrsalovic
sumber
3

Jika Anda bertanya

if not spam:
    print "Sorry. No SPAM."

yang __nonzero__ metode spam yang dipanggil. Dari manual Python:

__nonzero__ ( mandiri ) Dipanggil untuk menerapkan pengujian nilai kebenaran, dan bool operasi bawaan (); harus mengembalikan False atau True, atau setara bilangan bulatnya 0 atau 1. Ketika metode ini tidak didefinisikan, __len __ () dipanggil, jika didefinisikan (lihat di bawah). Jika sebuah kelas tidak mendefinisikan __len __ () atau __nonzero __ (), semua instansnya dianggap benar.

Jika Anda bertanya

if spam == None:
    print "Sorry. No SPAM here either."

yang __eq__ metode spam yang dipanggil dengan argumen ada .

Untuk informasi lebih lanjut tentang kemungkinan penyesuaian, lihat dokumentasi Python di https://docs.python.org/reference/datamodel.html#basic-customization

pi.
sumber
2

Kedua perbandingan ini memiliki tujuan yang berbeda. Yang pertama memeriksa nilai boolean dari sesuatu, yang kedua memeriksa identitas dengan nilai Tidak ada.

zgoda
sumber
1
Agar benar-benar benar, cek kedua untuk-kesetaraan- dengan nilai None ... "someobj is None" memeriksa identitas.
Matthew Trevor
0

Untuk satu contoh pertama lebih pendek dan terlihat lebih bagus. Seperti posting lainnya, apa yang Anda pilih juga tergantung pada apa yang benar-benar ingin Anda lakukan dengan perbandingan.

rslite
sumber
0

Jawabannya adalah, tergantung".

Saya menggunakan contoh pertama jika saya menganggap 0, "", [] dan False (daftar tidak lengkap) setara dengan Tidak ada dalam konteks ini.

Matthias Kestenholz
sumber
0

Secara pribadi, saya memilih pendekatan yang konsisten di seluruh bahasa: Saya lakukan if (var)(atau setara) hanya jika var dinyatakan sebagai boolean (atau didefinisikan seperti itu, di C kami tidak memiliki jenis tertentu). Saya bahkan mengawali variabel-variabel ini dengan b(jadi bVarsebenarnya) untuk memastikan saya tidak akan sengaja menggunakan tipe lain di sini.
Saya tidak terlalu suka casting implisit ke boolean, apalagi ketika ada banyak aturan kompleks.

Tentu saja, orang akan tidak setuju. Beberapa lebih jauh, saya melihat if (bVar == true)dalam kode Java di tempat kerja saya (terlalu berlebihan untuk seleraku!), Yang lain suka sintaksis terlalu banyak kompak, pergi while (line = getNextLine())(terlalu ambigu bagi saya).

PhiLho
sumber