jika A vs jika A tidak Tidak ada:

154

Bisakah saya menggunakan:

if A:

dari pada

if A is not None:

Yang terakhir tampaknya sangat bertele-tele. Apakah ada perbedaan?

rbairos
sumber

Jawaban:

149

Pernyataan

if A:

akan memanggil A.__nonzero__()(lihat dokumentasi Nama metode khusus ) dan menggunakan nilai balik dari fungsi itu. Berikut ringkasannya:

object.__nonzero__(self)

Dipanggil untuk menerapkan pengujian nilai kebenaran dan operasi internal bool(); harus mengembalikan Falseatau True, atau setara dengan bilangan bulatnya 0atau 1. Ketika metode ini tidak didefinisikan, __len__()dipanggil, jika didefinisikan, dan objek dianggap benar jika hasilnya nol. Jika suatu kelas tidak mendefinisikan __len__()atau pun tidak __nonzero__(), semua instance-nya dianggap benar.

Di samping itu,

if A is not None:

hanya membandingkan referensi Adengan Noneuntuk melihat apakah itu sama atau tidak.

Greg Hewgill
sumber
4
dan jadi A is not Nonelebih cepat karena ada jauh lebih sedikit pekerjaan yang harus dilakukan
John La Rooy
40
@gnibbler Tidak sesuai dengan tes saya. if object(): passadalah ~ 0,130 usec per loop, sedangkan if object() is not None: pass~ 0,135 usec. Pokoknya, Anda tidak boleh menggunakan kinerja untuk memilih di antara keduanya, melainkan melihat perbedaan dalam cara kerjanya, karena keduanya tidak setara .
Lauritz V. Thaulow
1
@gnibbler if A is not Nonetampaknya lebih lambat karena ini merupakan perbandingan dan perlu memuat singleton builtin Nonesebagai langkah perantara untuk membandingkan A(lihat di dis.dis()). Perbaiki saya jika saya salah tetapi if A:tampaknya lebih efisien, segera setelah Anda benar-benar ingin menguji nilai kebenaran dan bukan Noneidentitasnya.
cedbeu
2
@cedbeu, Tampaknya tergantung pada nilai A. Saya diuji sekarang python -m timeit -s"a=0" "if a: pass" "else: pass"lebih cepat daripada python -m timeit -s"a=0" "if a is None: pass" "else: pass"tetapi python -m timeit -s"a=1" "if a: pass" "else: pass"lebih lambat. Mungkin tergantung pada platform, lihat apakah Anda mendapatkan hasil yang sama
John La Rooy
2
@cedbeu, di Python3 mereka semua jauh lebih cepat, tetapi is Nonetes itu memang paling lambat bagi saya. Dalam pypy mereka semua mengukur persis sama :)
John La Rooy
51

Seperti yang tertulis dalam PEP8 :

  • Perbandingan dengan lajang seperti Tidak ada harus selalu dilakukan dengan 'adalah' atau 'tidak', tidak pernah operator kesetaraan .

    Juga, berhati-hatilah dalam menulis "jika x" ketika Anda benar-benar bermaksud "jika x tidak ada" - misalnya ketika menguji apakah suatu variabel atau argumen yang secara default adalah Tidak ada diatur ke nilai lain. Nilai lain mungkin memiliki tipe (seperti wadah) yang bisa salah dalam konteks boolean!

memo
sumber
7
Mmmh ini tidak terlalu jelas, dan tidak menanggapi OP. Pengalihan otomatis ke PEP tidak selalu merupakan jawaban yang baik. Sangat sering seseorang tidak tertarik pada tes identitas dengan singleton None, tetapi hanya dalam pengecekan nilai kebenaran. Dalam hal ini, if A:tampaknya lebih efisien (ambil a dis.dis(), ada langkah-langkah tambahan memuat builtin Nonedan membandingkan, dengan if A is not None:, sementara hanya ada jump_ifdalam kasus lain).
cedbeu
29
if x: #x is treated True except for all empty data types [],{},(),'',0 False, and None

jadi tidak sama dengan

if x is not None # which works only on None
Abdul Muneer
sumber
17

Banyak fungsi kembali Tidak ada jika tidak ada hasil yang sesuai. Misalnya, metode kueri SQLAlchemy .first()mengembalikan None jika tidak ada baris dalam hasilnya. Misalkan Anda memilih nilai yang mungkin mengembalikan 0 dan perlu tahu apakah itu sebenarnya 0 atau apakah kueri tidak memiliki hasil sama sekali.

Sebuah idiom umum adalah untuk memberikan argumen opsional fungsi atau metode nilai default Tidak ada, dan kemudian untuk menguji nilai yang menjadi Tidak ada untuk melihat apakah itu ditentukan. Sebagai contoh:

def spam(eggs=None):
    if eggs is None:
        eggs = retrievefromconfigfile()

bandingkan dengan:

def spam(eggs=None):
    if not eggs:
        eggs = retrievefromconfigfile()

Dalam yang terakhir, apa yang terjadi jika Anda menelepon spam(0)atau spam([])? Fungsi akan (secara keliru) mendeteksi bahwa Anda belum memberikan nilai untuk eggsdan akan menghitung nilai default untuk Anda. Mungkin itu bukan yang Anda inginkan.

Atau bayangkan metode seperti "kembalikan daftar transaksi untuk akun yang diberikan". Jika akun tidak ada, itu mungkin mengembalikan Tidak Ada. Ini berbeda dengan mengembalikan daftar kosong (yang berarti "akun ini ada tetapi belum mencatat transaksi).

Akhirnya, kembali ke basis data. Ada perbedaan besar antara NULL dan string kosong. String kosong biasanya mengatakan "ada nilai di sini, dan nilai itu tidak ada artinya sama sekali". NULL mengatakan "nilai ini belum dimasukkan."

Dalam setiap kasus tersebut, Anda ingin menggunakannya if A is None. Anda memeriksa nilai tertentu - Tidak ada - bukan hanya "nilai apa pun yang terjadi pada False".

Kirk Strauser
sumber
10

Mereka melakukan hal yang sangat berbeda .

Di bawah memeriksa apakah A memiliki apa-apa kecuali nilai-nilai False, [], None, ''dan 0. Ia memeriksa nilai A.

if A:

Di bawah ini memeriksa apakah A adalah objek yang berbeda dari Tidak Ada. Ia memeriksa dan membandingkan referensi (alamat memori) A dan None.

if A is not None:

PEMBARUAN: Penjelasan lebih lanjut

Sering kali keduanya tampaknya melakukan hal yang sama sehingga banyak orang menggunakannya secara bergantian - itu ide yang sangat buruk. Alasan keduanya memberikan hasil yang sama berkali-kali karena kebetulan murni karena optimalisasi penerjemah / kompiler seperti magang atau sesuatu yang lain.

Dengan adanya optimasi tersebut, bilangan bulat dan string dengan nilai yang sama berakhir menggunakan ruang memori yang sama. Itu mungkin menjelaskan mengapa dua string terpisah bertindak seolah-olah mereka sama.

> a = 'test'
> b = 'test'
> a is b
True
> a == b
True

Hal-hal lain tidak berperilaku sama ..

> a = []
> b = []
> a is b
False
> a == b
True

Kedua daftar jelas memiliki ingatan mereka sendiri. Tuple yang mengejutkan berperilaku seperti string.

> a = ()
> b = ()
> a is b
True
> a == b
True

Mungkin ini karena tuple dijamin tidak berubah, jadi masuk akal untuk menggunakan kembali memori yang sama.

Intinya adalah bahwa Anda tidak dapat bergantung pada kebetulan . Hanya karena dukun seperti bebek itu tidak berarti itu bebek. Gunakan isdan ==tergantung pada apa yang benar-benar ingin Anda periksa. Hal-hal ini bisa sulit untuk di-debug karena isterbaca seperti prosa yang sering kita selipkan saja.

Pithikos
sumber
Noneadalah singleton, ini bukan detail implementasi (tidak seperti int atau string magang). Saya tidak yakin saya mengikuti apa yang Anda maksud.
Lev Levitsky
@LevLevitsky maksud saya bukan yang Noneberperilaku berbeda dari intatau strkarena magang. Maksud saya adalah isdan ==memeriksa berbagai hal; pertama memeriksa alamat memori, yang kedua memeriksa isi alamat memori.
Pithikos
@LevLevitsky Saya mengedit jawaban saya sekarang untuk membuatnya lebih mudah dicerna.
Pithikos
7

if A: akan terbukti salah jika A adalah 0, False, string kosong, daftar kosong atau Tidak ada, yang dapat menyebabkan hasil yang tidak diinginkan.

keflavich
sumber
1
Dan banyak nilai lainnya juga, seperti daftar kosong, set kosong, kosong tuple dll. Pada dasarnya, apa pun yang tidak benar per docs.python.org/3/library/stdtypes.html#truth-value-testing .
jarmod
6

Sebagian besar panduan yang pernah saya lihat menyarankan agar Anda gunakan

jika sebuah:

kecuali Anda memiliki alasan untuk lebih spesifik.

Ada beberapa perbedaan kecil. Ada nilai selain Tidak ada yang mengembalikan False, misalnya daftar kosong, atau 0, jadi pikirkan apa yang sebenarnya Anda uji.

Colin Coghill
sumber
5

Tidak ada nilai khusus dalam Python yang biasanya menunjuk variabel tidak diinisialisasi. Untuk menguji apakah A tidak memiliki nilai khusus ini yang Anda gunakan:

if A is not None

Nilai-nilai Falsey adalah kelas objek khusus dalam Python (mis false, []). Untuk menguji apakah A palsu, gunakan:

if not A

Dengan demikian, kedua ungkapan itu tidak sama dan Anda sebaiknya tidak memperlakukannya sebagai sinonim.


PS None juga falsey, jadi ekspresi pertama menyiratkan yang kedua. Tetapi yang kedua mencakup nilai-nilai falsey lain selain Tidak Ada. Sekarang ... jika Anda dapat memastikan bahwa Anda tidak dapat memiliki nilai falsey lain selain None di A, maka Anda dapat mengganti ekspresi pertama dengan yang kedua.

mircealungu
sumber
Bukannya Anda salah, tetapi jawaban ini sudah mencakup ini.
Makoto
boh, saya ingin meminta Anda untuk menghapus downvote jika Anda tidak keberatan. jawaban saya setara dengan yang lain, mungkin kurang dengan yang Anda rujuk, tetapi disajikan dari sudut pandang yang berbeda, dan mungkin lebih mudah dipahami oleh seseorang. apalagi, ketika saya melihat downvote pada SO saya menganggap jawaban yang salah ... yang Anda akui tidak demikian.
mircealungu
Nah, anggapan itu tidak lengkap. Ini bisa salah atau tidak berguna. Karena jawaban Anda seperti yang saya sebutkan sudah dibahas, saya tidak yakin itu berguna.
Makoto
4

Itu tergantung pada konteksnya.

Saya menggunakan if A:ketika saya berharap Aakan menjadi semacam koleksi, dan saya hanya ingin menjalankan blok jika koleksi tidak kosong. Hal ini memungkinkan penelepon untuk melewati koleksi yang berperilaku baik, kosong atau tidak, dan membuatnya melakukan apa yang saya harapkan. Ini juga memungkinkan Nonedan Falseuntuk menekan eksekusi blok, yang kadang-kadang nyaman untuk memanggil kode.

OTOH, jika saya berharap Amenjadi objek yang sepenuhnya arbitrer tetapi bisa saja default None, maka saya selalu menggunakan if A is not None, karena kode panggilan bisa dengan sengaja memberikan referensi ke koleksi kosong, string kosong, atau tipe numerik bernilai 0, atau boolean False, atau beberapa instance kelas yang kebetulan salah dalam konteks boolean.

Dan di sisi lain, jika saya berharap Amenjadi beberapa hal yang lebih spesifik (misalnya instance dari kelas yang akan saya panggil metode), tapi itu bisa saja default None, dan saya menganggap konversi boolean default menjadi properti dari kelas yang saya tidak keberatan menerapkan pada semua subclass, maka saya hanya akan menggunakan if A:untuk menyelamatkan jari-jari saya dari mengetik 12 karakter ekstra.

Ben
sumber
3

Saya membuat file yang dipanggil test.pydan menjalankannya pada interpreter. Anda dapat mengubah apa yang Anda inginkan, untuk menguji dengan pasti bagaimana keadaan di balik layar.

import dis

def func1():

    matchesIterator = None

    if matchesIterator:

        print( "On if." );

def func2():

    matchesIterator = None

    if matchesIterator is not None:

        print( "On if." );

print( "\nFunction 1" );
dis.dis(func1)

print( "\nFunction 2" );
dis.dis(func2)

Ini adalah perbedaan assembler:

Sumber:

>>> import importlib
>>> reload( test )

Function 1
  6           0 LOAD_CONST               0 (None)
              3 STORE_FAST               0 (matchesIterator)

  8           6 LOAD_FAST                0 (matchesIterator)
              9 POP_JUMP_IF_FALSE       20

 10          12 LOAD_CONST               1 ('On if.')
             15 PRINT_ITEM
             16 PRINT_NEWLINE
             17 JUMP_FORWARD             0 (to 20)
        >>   20 LOAD_CONST               0 (None)
             23 RETURN_VALUE

Function 2
 14           0 LOAD_CONST               0 (None)
              3 STORE_FAST               0 (matchesIterator)

 16           6 LOAD_FAST                0 (matchesIterator)
              9 LOAD_CONST               0 (None)
             12 COMPARE_OP               9 (is not)
             15 POP_JUMP_IF_FALSE       26

 18          18 LOAD_CONST               1 ('On if.')
             21 PRINT_ITEM
             22 PRINT_NEWLINE
             23 JUMP_FORWARD             0 (to 26)
        >>   26 LOAD_CONST               0 (None)
             29 RETURN_VALUE
<module 'test' from 'test.py'>
pengguna
sumber
2

Yang pertama lebih Pythonic (kode ideomatik yang lebih baik), tetapi tidak akan mengeksekusi blok jika A adalah False (bukan None).

Borealid
sumber
7
-1. Seperti @klesh menyebutkan, PEP8 mengatakan untuk digunakan is/is not None. Mengikuti PEP8 adalah Pythonic. Selain itu kedua tes berbeda .
JCotton
1

python> = 2.6,

jika kita menulis seperti

if A:

akan menghasilkan peringatan seperti,

FutureWarning: Perilaku metode ini akan berubah di versi mendatang. Gunakan tes khusus 'len (elem)' atau 'elem bukan None'.

Jadi bisa kita gunakan

if A is not None:
Pengguna normal
sumber