Python: cách nhanh nhất để tạo danh sách n danh sách


97

Vì vậy, tôi đã tự hỏi làm thế nào để tạo một danh sách các danh sách trống tốt nhất:

[[],[],[]...]

Do cách Python hoạt động với danh sách trong bộ nhớ, điều này không hoạt động:

[[]]*n

Điều này tạo ra [[],[],...]nhưng mỗi phần tử là cùng một danh sách:

d = [[]]*n
d[0].append(1)
#[[1],[1],...]

Một cái gì đó giống như một công việc hiểu danh sách:

d = [[] for x in xrange(0,n)]

Nhưng điều này sử dụng máy ảo Python để lặp lại. Có cách nào để sử dụng một vòng lặp ngụ ý (lợi dụng nó được viết bằng C) không?

d = []
map(lambda n: d.append([]),xrange(0,10))

Điều này thực sự chậm hơn. :(


1
Tôi sẽ ngạc nhiên nếu có bất cứ điều gì về cơ bản nhanh hơn d = [[] for x in xrange(0,n)]. Bạn phải lặp lại một cách rõ ràng bằng Python hoặc gọi một hàm Python / lambda lặp đi lặp lại (sẽ chậm hơn). Nhưng vẫn hy vọng ai đó sẽ đăng một cái gì đó cho thấy tôi sai :).
MAK

1
Khi bạn đo những thứ này với timeit, bạn đã học được gì?
S.Lott

Tôi chỉ xác nhận rằng map(lambda x: [], xrange(n))chậm hơn so với việc hiểu danh sách.
Andrew Clark,

Câu trả lời:


105

Có lẽ cách duy nhất nhanh hơn một chút so với

d = [[] for x in xrange(n)]

from itertools import repeat
d = [[] for i in repeat(None, n)]

Nó không phải tạo một intđối tượng mới trong mỗi lần lặp lại và nhanh hơn khoảng 15% trên máy của tôi.

Chỉnh sửa : Sử dụng NumPy, bạn có thể tránh vòng lặp Python bằng cách sử dụng

d = numpy.empty((n, 0)).tolist()

nhưng điều này thực sự chậm hơn 2,5 lần so với việc hiểu danh sách.


Làm thế nào về map(lambda x:[], repeat(None,n))?
PaulMcG

3
@Paul: Điều đó sẽ chậm hơn một lần nữa do chi phí gọi hàm của biểu thức lambda.
Sven Marnach

Không nên cập nhật điều này ngay bây giờ mà phạm vi khác nhau trong Python 3?
beruic

@beruic Tôi chỉ trích dẫn mã từ câu hỏi trong khối mã đầu tiên, vì vậy không thực sự có ý nghĩa khi thay đổi điều đó.
Sven Marnach

12

Các comprehensions danh sách thực sự được thực hiện một cách hiệu quả hơn looping rõ ràng (xem các disđầu ra cho các chức năng ví dụ ) và mapcách có để gọi một đối tượng có thể được gọi ophaque trên mỗi lần lặp, mà phải gánh chịu chi phí trên không đáng kể.

Bất kể, [[] for _dummy in xrange(n)]là đúng cách để làm điều đó và không ai trong số các bé xíu (nếu tồn tại ở tất cả) chênh lệch tốc độ giữa các cách khác nhau nên quan trọng. Tất nhiên, trừ khi bạn dành phần lớn thời gian để làm việc này - nhưng trong trường hợp đó, bạn nên làm việc trên các thuật toán của mình. Bạn tạo những danh sách này thường xuyên như thế nào?


5
Xin vui lòng, không có _như một tên biến! Nếu không thì câu trả lời hay :)
Sven Marnach

11
@Sven: Tại sao không? Nó thường được sử dụng cho các biến không sử dụng (nếu nó được gọi i, tôi sẽ tìm kiếm nơi nó được sử dụng). Cạm bẫy duy nhất sẽ là nó che khuất việc _giữ kết quả cuối cùng trong REPL ... và đó chỉ là trường hợp 2.x nơi mà danh sách dễ hiểu bị rò rỉ.

Không thường xuyên, đó là lý do tại sao tôi đi trước và sử dụng khả năng hiểu danh sách. Tôi nghĩ rằng sẽ rất thú vị khi xem những gì mọi người phải nói. Tôi đã thấy Dropbox nói chuyện tại PyCon với việc sử dụng itertools.imap thay vì vòng lặp for để cập nhật hàm băm md5 rất nhiều lần và tôi hơi bị ám ảnh với các vòng lặp C kể từ đó.
munchybunch

12
Lý do quan trọng nhất để không sử dụng nó là nó có xu hướng gây nhầm lẫn cho mọi người, khiến họ nghĩ rằng nó là một loại cú pháp đặc biệt nào đó. Và ngoài xung đột với _trong trình thông dịch tương tác, nó cũng xung đột với bí danh gettext phổ biến. Nếu bạn muốn làm rõ rằng biến là một biến giả, hãy gọi nó dummy, không phải _.
Sven Marnach

12

Đây là hai phương pháp, một phương pháp ngọt ngào và đơn giản (và mang tính khái niệm), phương pháp kia chính thức hơn và có thể được mở rộng trong nhiều trường hợp khác nhau, sau khi đã đọc một tập dữ liệu.

Phương pháp 1: Khái niệm

X2=[]
X1=[1,2,3]
X2.append(X1)
X3=[4,5,6]
X2.append(X3)
X2 thus has [[1,2,3],[4,5,6]] ie a list of lists. 

Phương pháp 2: Trang trọng và có thể mở rộng

Một cách thanh lịch khác để lưu trữ danh sách dưới dạng danh sách các số khác nhau - danh sách này đọc từ một tệp. (Tệp ở đây có tập dữ liệu) Train là một tập dữ liệu có 50 hàng và 20 cột. I E. Train [0] cung cấp cho tôi hàng đầu tiên của tệp csv, train [1] cung cấp cho tôi hàng thứ 2, v.v. Tôi quan tâm đến việc tách tập dữ liệu có 50 hàng thành một danh sách, ngoại trừ cột 0, là biến được giải thích của tôi ở đây, vì vậy phải được xóa khỏi tập dữ liệu đào tạo gốc, và sau đó mở rộng danh sách sau danh sách - tức là danh sách một danh sách . Đây là mã thực hiện điều đó.

Lưu ý rằng tôi đang đọc từ "1" trong vòng lặp bên trong vì tôi chỉ quan tâm đến các biến giải thích. Và tôi khởi tạo lại X1 = [] trong vòng lặp khác, nếu không thì X2.append ([0: (len (train [0]) - 1)]) sẽ viết lại X1 nhiều lần - ngoài ra nó còn tiết kiệm bộ nhớ hơn.

X2=[]
for j in range(0,len(train)):
    X1=[]
    for k in range(1,len(train[0])):
        txt2=train[j][k]
        X1.append(txt2)
    X2.append(X1[0:(len(train[0])-1)])

4

Để tạo danh sách và danh sách sử dụng cú pháp bên dưới

     x = [[] for i in range(10)]

điều này sẽ tạo danh sách 1-d và để khởi tạo nó, hãy đặt số vào [[number] và đặt độ dài của độ dài đặt danh sách trong phạm vi (độ dài)

  • Để tạo danh sách các danh sách sử dụng cú pháp dưới đây.
    x = [[[0] for i in range(3)] for i in range(10)]

điều này sẽ khởi tạo danh sách danh sách với kích thước 10 * 3 và với giá trị 0

  • Để truy cập / thao tác phần tử
    x[1][5]=value

2

Vì vậy, tôi đã làm một số so sánh tốc độ để có được cách nhanh nhất. Việc hiểu danh sách thực sự rất nhanh. Cách duy nhất để đến gần là tránh mã bytecode bị tiết ra trong quá trình xây dựng danh sách. Nỗ lực đầu tiên của tôi là phương pháp sau, về nguyên tắc có vẻ nhanh hơn:

l = [[]]
for _ in range(n): l.extend(map(list,l))

(Tất nhiên, tạo ra một danh sách có độ dài 2 ** n) Việc xây dựng này chậm gấp đôi so với việc hiểu danh sách, theo thời gian, cho cả danh sách ngắn và dài (một triệu).

Nỗ lực thứ hai của tôi là sử dụng starmap để gọi hàm tạo danh sách đối với tôi, Có một cấu trúc dường như chạy hàm tạo danh sách ở tốc độ cao nhất, nhưng vẫn chậm hơn, nhưng chỉ bằng một lượng nhỏ:

from itertools import starmap
l = list(starmap(list,[()]*(1<<n)))

Điều thú vị là thời gian thực thi cho thấy rằng chính lệnh gọi danh sách cuối cùng đã làm cho giải pháp bản đồ sao chậm, vì thời gian thực thi của nó gần như chính xác bằng tốc độ của:

l = list([] for _ in range(1<<n))

Lần thử thứ ba của tôi đến khi tôi nhận ra rằng danh sách (()) cũng tạo ra một danh sách, vì vậy tôi đã thử ứng dụng thực sự đơn giản:

l = list(map(list, [()]*(1<<n)))

nhưng điều này chậm hơn lệnh gọi bản đồ sao.

Kết luận: đối với những người cuồng tốc độ: Hãy sử dụng khả năng hiểu danh sách. Chỉ gọi các chức năng, nếu bạn phải. Sử dụng nội trang.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.