Câu trả lời:
Đó là một mảng động . Bằng chứng thực tế: Việc lập chỉ mục mất (tất nhiên là có sự khác biệt cực kỳ nhỏ (0,0013 Cách sử dụng!)) Cùng một lúc bất kể chỉ số:
...>python -m timeit --setup="x = [None]*1000" "x[500]"
10000000 loops, best of 3: 0.0579 usec per loop
...>python -m timeit --setup="x = [None]*1000" "x[0]"
10000000 loops, best of 3: 0.0566 usec per loop
Tôi sẽ ngạc nhiên nếu IronPython hoặc Jython sử dụng danh sách được liên kết - chúng sẽ phá hỏng hiệu suất của nhiều thư viện được sử dụng rộng rãi được xây dựng dựa trên giả định rằng danh sách là mảng động.
x=[None]*1000
, để lại sự đo lường của bất kỳ sự khác biệt truy cập danh sách có thể thay vì không chính xác. Bạn cần tách riêng phần khởi tạo:-s "x=[None]*100" "x[0]"
Mã C khá đơn giản, thực sự. Mở rộng một macro và cắt tỉa một số ý kiến không liên quan, cấu trúc cơ bản nằm trong listobject.h
đó xác định danh sách là:
typedef struct {
PyObject_HEAD
Py_ssize_t ob_size;
/* Vector of pointers to list elements. list[0] is ob_item[0], etc. */
PyObject **ob_item;
/* ob_item contains space for 'allocated' elements. The number
* currently in use is ob_size.
* Invariants:
* 0 <= ob_size <= allocated
* len(list) == ob_size
* ob_item == NULL implies ob_size == allocated == 0
*/
Py_ssize_t allocated;
} PyListObject;
PyObject_HEAD
chứa số tham chiếu và định danh loại. Vì vậy, nó là một vectơ / mảng tổng thể. Mã để thay đổi kích thước một mảng như vậy khi nó đầy listobject.c
. Nó không thực sự tăng gấp đôi mảng, nhưng phát triển bằng cách phân bổ
new_allocated = (newsize >> 3) + (newsize < 9 ? 3 : 6);
new_allocated += newsize;
đến công suất mỗi lần, newsize
kích thước được yêu cầu ở đâu (không nhất thiết allocated + 1
bởi vì bạn có thể extend
bằng một số phần tử tùy ý thay vìappend
'từng cái một).
Xem thêm Câu hỏi thường gặp về Python .
array
mô-đun hoặc NumPy sẽ được ưu tiên.
Điều này phụ thuộc vào việc triển khai, nhưng IIRC:
ArrayList
Do đó, tất cả họ đều có quyền truy cập ngẫu nhiên O (1).
O(1)
lập chỉ mục danh sách là một giả định khá phổ biến và hợp lệ, nên không có triển khai nào dám phá vỡ nó.
Tôi muốn đề xuất bài viết "Thực hiện danh sách Python" của Laurent Luce . Nó thực sự hữu ích cho tôi vì tác giả giải thích cách danh sách được triển khai trong CPython và sử dụng các sơ đồ tuyệt vời cho mục đích này.
Liệt kê cấu trúc đối tượng C
Một đối tượng danh sách trong CPython được biểu diễn bằng cấu trúc C sau.
ob_item
là một danh sách các con trỏ đến các thành phần danh sách. phân bổ là số lượng khe được phân bổ trong bộ nhớ.typedef struct { PyObject_VAR_HEAD PyObject **ob_item; Py_ssize_t allocated; } PyListObject;
Điều quan trọng là nhận thấy sự khác biệt giữa các vị trí được phân bổ và kích thước của danh sách. Kích thước của một danh sách là giống như
len(l)
. Số lượng vị trí được phân bổ là những gì đã được phân bổ trong bộ nhớ. Thông thường, bạn sẽ thấy rằng phân bổ có thể lớn hơn kích thước. Điều này là để tránh cần gọirealloc
mỗi khi một yếu tố mới được thêm vào danh sách.
...
Nối
Chúng tôi nối một số nguyên vào danh sách :
l.append(1)
. Chuyện gì xảy ra
Chúng tôi tiếp tục bằng cách thêm một yếu tố nữa :
l.append(2)
.list_resize
được gọi với n + 1 = 2 nhưng vì kích thước được phân bổ là 4 nên không cần phân bổ thêm bộ nhớ. Điều tương tự cũng xảy ra khi chúng ta bổ sung thêm 2 số nguyên:l.append(3)
,l.append(4)
. Sơ đồ sau đây cho thấy những gì chúng ta có cho đến nay.
...
Chèn
Hãy chèn một số nguyên mới (5) vào vị trí 1:
l.insert(1,5)
và xem xét những gì xảy ra trong nội bộ.
...
Nhạc pop
Khi bạn bật phần tử cuối cùng :
l.pop()
,listpop()
được gọi.list_resize
được gọi bên tronglistpop()
và nếu kích thước mới nhỏ hơn một nửa kích thước được phân bổ thì danh sách bị thu hẹp.Bạn có thể quan sát rằng khe 4 vẫn trỏ đến số nguyên nhưng điều quan trọng là kích thước của danh sách hiện là 4. Hãy bật thêm một yếu tố. Trong
list_resize()
, kích thước - 1 = 4 - 1 = 3 nhỏ hơn một nửa số vị trí được phân bổ để danh sách được thu nhỏ thành 6 vị trí và kích thước mới của danh sách hiện là 3.Bạn có thể quan sát rằng khe 3 và 4 vẫn trỏ đến một số số nguyên nhưng điều quan trọng là kích thước của danh sách hiện là 3.
...
Xóa đối tượng danh sách Python có một phương thức để loại bỏ một phần tử cụ thể :
l.remove(5)
.
aggregation
, không composition
. Tôi ước có một danh sách các thành phần quá.
Như những người khác đã nêu ở trên, các danh sách (khi đáng kể lớn) được thực hiện bằng cách phân bổ một lượng không gian cố định và, nếu không gian đó sẽ lấp đầy, hãy phân bổ một lượng không gian lớn hơn và sao chép qua các phần tử.
Để hiểu lý do tại sao phương thức được khấu hao O (1), không mất tính tổng quát, giả sử chúng ta đã chèn các phần tử a = 2 ^ n và bây giờ chúng ta phải tăng gấp đôi bảng của mình lên kích thước 2 ^ (n + 1). Điều đó có nghĩa là chúng tôi hiện đang thực hiện các hoạt động 2 ^ (n + 1). Bản sao cuối cùng, chúng tôi đã thực hiện 2 ^ n hoạt động. Trước đó, chúng tôi đã làm 2 ^ (n-1) ... tất cả đều giảm xuống còn 8.4,2,1. Bây giờ, nếu chúng ta thêm những thứ này lên, chúng ta sẽ nhận được 1 + 2 + 4 + 8 + ... + 2 ^ (n + 1) = 2 ^ (n + 2) - 1 <4 * 2 ^ n = O (2 ^ n) = O (a) tổng số lần chèn (tức là thời gian khấu hao O (1)). Ngoài ra, cần lưu ý rằng nếu bảng cho phép xóa, việc thu hẹp bảng phải được thực hiện ở một yếu tố khác (ví dụ 3x)
Một danh sách trong Python là một cái gì đó giống như một mảng, nơi bạn có thể lưu trữ nhiều giá trị. Danh sách có thể thay đổi có nghĩa là bạn có thể thay đổi nó. Điều quan trọng hơn bạn nên biết, khi chúng tôi tạo một danh sách, Python sẽ tự động tạo một Reference_id cho biến danh sách đó. Nếu bạn thay đổi nó bằng cách gán biến khác, danh sách chính sẽ thay đổi. Hãy thử với một ví dụ:
list_one = [1,2,3,4]
my_list = list_one
#my_list: [1,2,3,4]
my_list.append("new")
#my_list: [1,2,3,4,'new']
#list_one: [1,2,3,4,'new']
Chúng tôi chắp thêm my_list
nhưng danh sách chính của chúng tôi đã thay đổi. Danh sách đó có nghĩa là không được gán làm danh sách sao chép gán làm tham chiếu của nó.