Tại sao []
nhanh hơn list()
?
Lý do lớn nhất là Python xử lý list()
giống như một hàm do người dùng định nghĩa, có nghĩa là bạn có thể chặn nó bằng cách đặt bí danh cho thứ kháclist
và làm một cái gì đó khác (như sử dụng danh sách phân lớp của riêng bạn hoặc có lẽ là một deque).
Nó ngay lập tức tạo ra một phiên bản mới của danh sách dựng sẵn với []
.
Giải thích của tôi tìm cách cung cấp cho bạn trực giác cho việc này.
Giải trình
[]
thường được gọi là cú pháp theo nghĩa đen.
Trong ngữ pháp, điều này được gọi là "hiển thị danh sách". Từ các tài liệu :
Hiển thị danh sách là một chuỗi các biểu thức có thể trống được đặt trong dấu ngoặc vuông:
list_display ::= "[" [starred_list | comprehension] "]"
Hiển thị danh sách mang lại một đối tượng danh sách mới, nội dung được chỉ định bởi danh sách biểu thức hoặc mức độ hiểu. Khi một danh sách các biểu thức được phân tách bằng dấu phẩy được cung cấp, các phần tử của nó được ước tính từ trái sang phải và được đặt vào đối tượng danh sách theo thứ tự đó. Khi một sự hiểu biết được cung cấp, danh sách được xây dựng từ các yếu tố kết quả từ sự hiểu biết.
Nói tóm lại, điều này có nghĩa là một đối tượng dựng sẵn của kiểu list
được tạo.
Không có cách nào phá vỡ điều này - điều đó có nghĩa là Python có thể làm điều đó nhanh nhất có thể.
Mặt khác, list()
có thể bị chặn khỏi việc tạo nội dung list
bằng cách sử dụng hàm tạo danh sách dựng sẵn.
Ví dụ: giả sử chúng tôi muốn danh sách của mình được tạo ra một cách ồn ào:
class List(list):
def __init__(self, iterable=None):
if iterable is None:
super().__init__()
else:
super().__init__(iterable)
print('List initialized.')
Sau đó, chúng tôi có thể chặn tên list
trên phạm vi toàn cầu ở cấp mô-đun và sau đó khi chúng tôi tạo một list
, chúng tôi thực sự tạo danh sách phụ của chúng tôi:
>>> list = List
>>> a_list = list()
List initialized.
>>> type(a_list)
<class '__main__.List'>
Tương tự như vậy, chúng ta có thể loại bỏ nó khỏi không gian tên toàn cầu
del list
và đặt nó trong không gian tên dựng sẵn:
import builtins
builtins.list = List
Và bây giờ:
>>> list_0 = list()
List initialized.
>>> type(list_0)
<class '__main__.List'>
Và lưu ý rằng hiển thị danh sách tạo ra một danh sách vô điều kiện:
>>> list_1 = []
>>> type(list_1)
<class 'list'>
Chúng tôi có lẽ chỉ làm điều này tạm thời, vì vậy hãy hoàn tác các thay đổi của chúng tôi - trước tiên hãy xóa List
đối tượng mới khỏi các nội trang:
>>> 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
Ồ, không, chúng tôi mất dấu vết của bản gốc.
Đừng lo lắng, chúng ta vẫn có thể nhận được list
- đó là loại danh sách theo nghĩa đen:
>>> builtins.list = type([])
>>> list()
[]
Vì thế...
Tại sao []
nhanh hơn list()
?
Như chúng ta đã thấy - chúng ta có thể ghi đè lên list
- nhưng chúng ta không thể chặn việc tạo ra kiểu chữ. Khi chúng tôi sử dụng, list
chúng tôi phải thực hiện tra cứu để xem có gì ở đó không.
Sau đó, chúng tôi phải gọi bất cứ điều gì có thể gọi chúng tôi đã tìm kiếm. Từ ngữ pháp:
Một cuộc gọi gọi một đối tượng có thể gọi được (ví dụ: một hàm) với một loạt các đối số có thể trống:
call ::= primary "(" [argument_list [","] | comprehension] ")"
Chúng ta có thể thấy rằng nó làm điều tương tự cho bất kỳ tên nào, không chỉ danh sách:
>>> 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
Vì []
không có chức năng gọi ở cấp độ mã byte Python:
>>> dis.dis('[]')
1 0 BUILD_LIST 0
2 RETURN_VALUE
Nó chỉ đơn giản là đi thẳng vào việc xây dựng danh sách mà không có bất kỳ tra cứu hay cuộc gọi nào ở cấp độ mã byte.
Phần kết luận
Chúng tôi đã chứng minh rằng list
có thể chặn mã người dùng bằng cách sử dụng các quy tắc phạm vi và list()
tìm kiếm một cuộc gọi và sau đó gọi nó.
Trong khi đó []
là một hiển thị danh sách, hoặc một nghĩa đen, và do đó tránh việc tra cứu tên và gọi hàm.
()
và''
thật đặc biệt, vì chúng không chỉ trống rỗng, chúng còn bất biến, và như vậy, đó là một chiến thắng dễ dàng để biến chúng thành những người độc thân; họ thậm chí không xây dựng các đối tượng mới, chỉ tải các đơn vị trốngtuple
/str
. Về mặt kỹ thuật là một chi tiết triển khai, nhưng tôi có một thời gian khó tưởng tượng tại sao họ sẽ không lưu trữ trốngtuple
/str
vì lý do hiệu suất. Vì vậy, trực giác của bạn về[]
và{}
trả lại một nghĩa đen chứng khoán là sai, nhưng nó áp dụng cho()
và''
.