>>> timeit.timeit("'x' in ('x',)")
0.04869917374131205
>>> timeit.timeit("'x' == 'x'")
0.06144205736110564
Juga berfungsi untuk tupel dengan banyak elemen, kedua versi ini tampaknya tumbuh secara linear:
>>> timeit.timeit("'x' in ('x', 'y')")
0.04866674801541748
>>> timeit.timeit("'x' == 'x' or 'x' == 'y'")
0.06565782838087131
>>> timeit.timeit("'x' in ('y', 'x')")
0.08975995576448526
>>> timeit.timeit("'x' == 'y' or 'x' == 'y'")
0.12992391047427532
Berdasarkan ini, saya pikir saya harus benar - benar mulai menggunakan di in
mana - mana ==
!
python
performance
python-3.x
python-internals
Markus Meskanen
sumber
sumber
in
mana - mana==
. Ini adalah pengoptimalan prematur yang merusak keterbacaan.x ="!foo"
x in ("!foo",)
danx == "!foo"
in
alih-alih==
adalah beralih ke C.Jawaban:
Seperti yang saya sebutkan kepada David Wolever, ada lebih dari ini yang bisa dilihat; kedua metode dikirim ke
is
; Anda dapat membuktikan ini dengan melakukanYang pertama hanya bisa sangat cepat karena memeriksa dengan identitas.
Untuk mengetahui mengapa yang satu lebih lama dari yang lain, mari kita telusuri eksekusi.
Mereka berdua mulai
ceval.c
,COMPARE_OP
sejak itu adalah bytecode yang terlibatIni muncul nilai dari tumpukan (secara teknis hanya muncul satu)
dan menjalankan pembandingan:
cmp_outcome
Apakah ini:Di sinilah jalur dibagi. The
PyCmp_IN
cabang tidakPerhatikan bahwa tuple didefinisikan sebagai
Jadi rantingnya
akan diambil dan
*sqm->sq_contains
, yang merupakan fungsinya(objobjproc)tuplecontains
, akan diambil.Ini tidak
... Tunggu, bukankah itu
PyObject_RichCompareBool
yang diambil cabang lain? Tidak, itu tadiPyObject_RichCompare
.Jalur kode itu pendek sehingga kemungkinan hanya sampai pada kecepatan keduanya. Mari kita bandingkan.
Jalur kode di
PyObject_RichCompareBool
hampir segera berakhir. UntukPyObject_RichCompare
ituThe
Py_EnterRecursiveCall
/Py_LeaveRecursiveCall
combo tidak diambil di jalur sebelumnya, tetapi ini adalah makro yang relatif cepat yang akan pendek-sirkuit setelah incrementing dan decrementing beberapa GLOBALS.do_richcompare
tidak:Ini melakukan beberapa pemeriksaan cepat untuk memanggil
v->ob_type->tp_richcompare
yang manayang mana
Yaitu, pintasan ini di
left == right
... tetapi hanya setelah dilakukanSemua dalam semua jalur kemudian terlihat seperti ini (secara manual secara inuratif, membuka gulungan dan memangkas cabang yang dikenal)
vs.
Sekarang,
PyUnicode_Check
danPyUnicode_READY
cukup murah karena mereka hanya memeriksa beberapa bidang, tetapi harus jelas bahwa yang paling atas adalah jalur kode yang lebih kecil, ia memiliki lebih sedikit pemanggilan fungsi, hanya satu pergantian pernyataan dan hanya sedikit lebih tipis.TL; DR:
Keduanya mengirim ke
if (left_pointer == right_pointer)
; perbedaannya hanyalah seberapa banyak pekerjaan yang mereka lakukan untuk sampai ke sana.in
hanya kurang.sumber
Ada tiga faktor yang berperan di sini yang, jika digabungkan, menghasilkan perilaku mengejutkan ini.
Pertama:
in
operator mengambil jalan pintas dan memeriksa identitas (x is y
) sebelum memeriksa kesetaraan (x == y
):Kedua: karena string Python diinternir, keduanya
"x"
dalam"x" in ("x", )
akan identik:(peringatan besar: ini adalah perilaku khusus implementasi!
is
Jangan pernah digunakan untuk membandingkan string karena terkadang akan memberikan jawaban yang mengejutkan; misalnya"x" * 100 is "x" * 100 ==> False
)Ketiga: sebagaimana dirinci dalam jawaban fantastis Veedrac ,
tuple.__contains__
( kirax in (y, )
- kira setara dengan(y, ).__contains__(x)
) sampai pada titik melakukan pemeriksaan identitas lebih cepat daripadastr.__eq__
(sekali lagi, kirax == y
- kira setara denganx.__eq__(y)
).Anda dapat melihat bukti untuk ini karena
x in (y, )
secara signifikan lebih lambat dari logis setara,x == y
:The
x in (y, )
kasus lambat karena, setelahis
perbandingan gagal,in
operator yang jatuh kembali ke memeriksa kesetaraan normal (yaitu, menggunakan==
), sehingga perbandingan mengambil tentang jumlah waktu yang sama seperti==
, rendering seluruh operasi lebih lambat karena overhead menciptakan tupel , berjalan anggotanya, dll.Perhatikan juga bahwa
a in (b, )
itu hanya lebih cepat ketikaa is b
:(Mengapa
a in (b, )
lebih cepat daripadaa is b or a == b
? Dugaan saya akan lebih sedikit instruksi mesin virtual -a in (b, )
hanya ~ 3 instruksi, di manaa is b or a == b
akan ada lebih banyak instruksi VM)Jawaban Veedrac - https://stackoverflow.com/a/28889838/71522 - menjelaskan lebih detail tentang apa yang terjadi pada masing-masing
==
danin
dan layak dibaca.sumber
X in [X,Y,Z]
untuk bekerja dengan benar tanpaX
,,Y
atauZ
harus mendefinisikan metode kesetaraan (atau lebih tepatnya, kesetaraan standaris
, sehingga menghemat harus memanggil__eq__
objek tanpa ditentukan pengguna__eq__
danis
menjadi benar harus menyiratkan nilai -persamaan).float('nan')
potensi menyesatkan. Ini adalah propertinan
yang tidak sama dengan dirinya sendiri. Itu dapat mengubah waktunya.in
diambil pada tes keanggotaan. Saya akan mengubah nama variabel untuk memperjelas.tuple.__contains__
diimplementasikan olehtuplecontains
yang memanggilPyObject_RichCompareBool
dan yang segera kembali dalam kasus identitas.unicode
ada diPyUnicode_RichCompare
bawah tenda, yang memiliki pintasan yang sama untuk identitas."x" is "x"
belum tentu demikianTrue
.'x' in ('x', )
akan selalu adaTrue
, tetapi tampaknya tidak lebih cepat dari==
.