Kenapa []
lebih cepat dari itu list()
?
Alasan terbesarnya adalah bahwa Python memperlakukan list()
seperti fungsi yang ditentukan pengguna, yang berarti Anda dapat mencegatnya dengan mengalihkan sesuatu yang lain ke list
dan melakukan sesuatu yang berbeda (seperti menggunakan daftar subklas Anda sendiri atau mungkin deque).
Segera membuat contoh baru dari daftar builtin dengan []
.
Penjelasan saya berusaha memberi Anda intuisi untuk ini.
Penjelasan
[]
umumnya dikenal sebagai sintaksis literal.
Dalam tata bahasa, ini disebut sebagai "tampilan daftar". Dari dokumen :
Tampilan daftar adalah serangkaian ekspresi yang mungkin kosong yang terlampir dalam tanda kurung:
list_display ::= "[" [starred_list | comprehension] "]"
Tampilan daftar menghasilkan objek daftar baru, konten yang ditentukan oleh daftar ekspresi atau pemahaman. Ketika daftar ekspresi yang dipisahkan koma disediakan, elemen-elemennya dievaluasi dari kiri ke kanan dan ditempatkan ke objek daftar dalam urutan itu. Ketika pemahaman diberikan, daftar dibangun dari elemen-elemen yang dihasilkan dari pemahaman.
Singkatnya, ini berarti bahwa objek tipe builtin list
dibuat.
Tidak ada yang bisa mengelak dari ini - yang berarti Python dapat melakukannya secepat mungkin.
Di sisi lain, list()
dapat dicegat dari membuat builtin list
menggunakan konstruktor daftar builtin.
Misalnya, kami ingin daftar kami dibuat dengan berisik:
class List(list):
def __init__(self, iterable=None):
if iterable is None:
super().__init__()
else:
super().__init__(iterable)
print('List initialized.')
Kami kemudian dapat mencegat nama list
pada lingkup global level modul, dan kemudian ketika kami membuat list
, kami benar-benar membuat daftar subtipe kami:
>>> list = List
>>> a_list = list()
List initialized.
>>> type(a_list)
<class '__main__.List'>
Demikian pula kita dapat menghapusnya dari namespace global
del list
dan letakkan di namespace builtin:
import builtins
builtins.list = List
Dan sekarang:
>>> list_0 = list()
List initialized.
>>> type(list_0)
<class '__main__.List'>
Dan perhatikan bahwa tampilan daftar membuat daftar tanpa syarat:
>>> list_1 = []
>>> type(list_1)
<class 'list'>
Kami mungkin hanya melakukan ini sementara, jadi mari kita batalkan perubahan kami - pertama-tama hapus List
objek baru dari bawaan:
>>> del builtins.list
>>> builtins.list
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: module 'builtins' has no attribute 'list'
>>> list()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'list' is not defined
Oh, tidak, kami kehilangan jejak aslinya.
Tidak perlu khawatir, kita masih bisa mendapatkan list
- itu adalah jenis daftar literal:
>>> builtins.list = type([])
>>> list()
[]
Begitu...
Kenapa []
lebih cepat dari itu list()
?
Seperti yang telah kita lihat - kita dapat menimpa list
- tetapi kita tidak dapat mencegat penciptaan tipe literal. Ketika kita menggunakan list
kita harus melakukan pencarian untuk melihat apakah ada sesuatu di sana.
Maka kita harus memanggil panggilan apa pun yang kita cari. Dari tata bahasa:
Panggilan memanggil objek yang dapat dipanggil (misalnya, fungsi) dengan serangkaian argumen yang mungkin kosong:
call ::= primary "(" [argument_list [","] | comprehension] ")"
Kita dapat melihat bahwa ia melakukan hal yang sama untuk nama apa pun, tidak hanya daftar:
>>> import dis
>>> dis.dis('list()')
1 0 LOAD_NAME 0 (list)
2 CALL_FUNCTION 0
4 RETURN_VALUE
>>> dis.dis('doesnotexist()')
1 0 LOAD_NAME 0 (doesnotexist)
2 CALL_FUNCTION 0
4 RETURN_VALUE
Karena []
tidak ada panggilan fungsi di tingkat bytecode Python:
>>> dis.dis('[]')
1 0 BUILD_LIST 0
2 RETURN_VALUE
Itu hanya langsung membangun daftar tanpa pencarian atau panggilan di tingkat bytecode.
Kesimpulan
Kami telah menunjukkan bahwa list
dapat dicegat dengan kode pengguna menggunakan aturan pelingkupan, dan yang list()
mencari callable dan kemudian menyebutnya.
Sedangkan []
tampilan daftar, atau literal, dan dengan demikian menghindari pencarian nama dan panggilan fungsi.
()
dan''
istimewa, karena tidak hanya kosong, mereka juga tidak berubah, dan karenanya, mudah untuk membuatnya menjadi lajang; mereka bahkan tidak membuat objek baru, hanya memuat singleton untuk yang kosongtuple
/str
. Secara teknis detail implementasi, tapi saya kesulitan membayangkan mengapa mereka tidak melakukan cache kosongtuple
/str
untuk alasan kinerja. Jadi intuisi Anda tentang[]
dan{}
mengembalikan stok literal adalah salah, tetapi itu berlaku untuk()
dan''
.