Cách Pythonic để trả về danh sách của mọi mục thứ n trong danh sách lớn hơn


170

Giả sử chúng tôi có một danh sách các số từ 0 đến 1000. Có cách nào hiệu quả / hiệu quả để tạo danh sách các mục đầu tiên và thứ 10 tiếp theo, tức là [0, 10, 20, 30, ... ] không?

Vâng, tôi có thể làm điều này bằng cách sử dụng vòng lặp for, nhưng tôi tự hỏi liệu có cách nào gọn gàng hơn để làm điều này không, thậm chí có thể trong một dòng?

Câu trả lời:


289
>>> lst = list(range(165))
>>> lst[0::10]
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]

Lưu ý rằng điều này nhanh hơn khoảng 100 lần so với việc lặp và kiểm tra một mô-đun cho từng phần tử:

$ python -m timeit -s "lst = list(range(1000))" "lst1 = [x for x in lst if x % 10 == 0]"
1000 loops, best of 3: 525 usec per loop
$ python -m timeit -s "lst = list(range(1000))" "lst1 = lst[0::10]"
100000 loops, best of 3: 4.02 usec per loop

4
Chắc chắn, sự hiểu biết danh sách là mạnh mẽ hơn nói chung. OTOH, câu hỏi đặt ra một danh sách hiện có và, trong trường hợp đó, một lát cắt hoạt động tốt.
Ned Deily

Tôi nhận xét về điều này dưới đây trong danh sách câu trả lời hiểu. Hãy cẩn thận với "nếu x% 10 == 0". Nó chỉ hoạt động với ví dụ danh sách cụ thể này, nhưng nếu danh sách đầu vào là ví dụ l = phạm vi (0,1000,2) thì nó sẽ không rút ra mỗi mục thứ 10.
Andre Miller

12
@Andre: rất đúng. Vì vậy, đây là một ví dụ về tính năng ngôn ngữ khiêm tốn, toán tử lát, hóa ra trong trường hợp này (1) để giúp dễ dàng có được kết quả chính xác; (2) kết quả trong một biểu thức ngắn gọn hơn; và (3) xảy ra nhanh hơn 2 bậc. (1) cho đến nay là mối quan tâm quan trọng nhất, nhưng, nhờ thiết kế cẩn thận và thực hiện ngôn ngữ, bạn có được cả ba với giá 1. Câu hỏi và câu trả lời hay.
Ned Deily

2
0dư thừa trong l[0::10]. l[::10]dễ đọc hơn, ít nhầm lẫn hơn.
Konstantin Schubert

Tôi ngạc nhiên khi so sánh hiệu suất 0,5 giây để hiểu danh sách và 0,4 giây cho danh sách lát. Có vẻ rất chậm, tại sao cắt danh sách cần 100 nghìn vòng cho danh sách cỡ 1 nghìn!?
Damo

57
  1. source_list[::10] là rõ ràng nhất, nhưng điều này không hoạt động đối với bất kỳ lần lặp nào và không hiệu quả bộ nhớ cho các danh sách lớn.
  2. itertools.islice(source_sequence, 0, None, 10) hoạt động cho bất kỳ lần lặp nào và có hiệu quả bộ nhớ, nhưng có lẽ không phải là giải pháp nhanh nhất cho danh sách lớn và bước tiến lớn.
  3. (source_list[i] for i in xrange(0, len(source_list), 10))

1
+1 Câu trả lời hay nhất, IMO. Tất cả ba đề xuất một giải pháp chung (ví dụ: lấy danh sách nguồn làm cho). Giải pháp trình tạo (3.) là tốt khi nó lọc trên chỉ mục của danh sách nguồn. Nó có thể có hiệu quả bộ nhớ là 2. Cả hai chỉ số và danh sách kết quả đều là các trình tạo và do đó được xây dựng một cách lười biếng, đây cũng có thể là nhanh nhất nếu bạn không cần danh sách kết quả trong một đoạn duy nhất. Chỉ khi danh sách nguồn có thể là một trình tạo, tôi sẽ đi với thành ngữ "Paul, i trong liệt kê (l)", vì không có len () của trình tạo. BTW, loại lặp nào sẽ không hoạt động với 1.? Máy phát điện?!
ThomasH

Iterable = object với phương thức __iter __ () trả về iterator (object với phương thức next ())
Denis Otkidach

24

Bạn có thể sử dụng toán tử lát như thế này:

l = [1,2,3,4,5]
l2 = l[::2] # get subsequent 2nd item

Làm thế nào để có được mỗi mục thứ 2 bắt đầu từ thứ 3?
user1993

4
@ user1993L[2::2]
Nick Dandoulakis

19

Từ hướng dẫn: s[i:j:k] slice of s from i to j with step k

li = range(100)
sub = li[0::10]

>>> sub
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90]

13
newlist = oldlist[::10]

Điều này chọn ra mọi yếu tố thứ 10 của danh sách.


4

Tại sao không chỉ sử dụng một tham số bước của chức năng phạm vi để có được:

l = range(0, 1000, 10)

Để so sánh, trên máy của tôi:

H:\>python -m timeit -s "l = range(1000)" "l1 = [x for x in l if x % 10 == 0]"
10000 loops, best of 3: 90.8 usec per loop
H:\>python -m timeit -s "l = range(1000)" "l1 = l[0::10]"
1000000 loops, best of 3: 0.861 usec per loop
H:\>python -m timeit -s "l = range(0, 1000, 10)"
100000000 loops, best of 3: 0.0172 usec per loop

3
@SilentGhost: Điều đó đúng, nhưng vì đây là câu hỏi dành cho người mới bắt đầu nên chức năng phạm vi có thể là điều họ thực sự muốn làm, vì vậy tôi nghĩ đó là một câu trả lời hợp lệ. (Mặc dù giới hạn trên phải là 1001, không phải 1000)
Scott Griffiths

2
existing_list = range(0, 1001)
filtered_list = [i for i in existing_list if i % 10 == 0]

1
Tại sao bạn có mệnh đề if khi phạm vi (0, 1001, 10) chỉ lấy mỗi phần tử thứ 10?
Autoplectic

4
Cùng một nhận xét ở đây, điều này không giải quyết được vấn đề chung hơn về "Cách Pythonic để trả về danh sách của mọi mục thứ n trong danh sách lớn hơn", giải pháp của bạn phụ thuộc vào thực tế là các giá trị của danh sách ví dụ là 0 đến 1000 và chỉ kéo các mục trong danh sách có giá trị chia hết cho 10 thay vì mỗi mục thứ 10.
Andre Miller

1
Vâng, OP viết: "chúng tôi có một danh sách các số từ 0 đến 1000". Vì vậy, anh ta không cần một giải pháp chung.

1
Ông viết 'Hãy nói rằng chúng ta có ..' ngụ ý nó chỉ là một ví dụ. Nếu anh ta thực sự muốn mỗi số thứ 10 trong danh sách từ 0 đến 1000 thì câu trả lời sẽ là phạm vi (0,1001,10) hoặc một cái gì đó tương tự.
Andre Miller

1

Dưới đây là cách triển khai tốt hơn để hiểu danh sách "mọi mục thứ 10", không sử dụng nội dung danh sách như một phần của bài kiểm tra thành viên:

>>> l = range(165)
>>> [ item for i,item in enumerate(l) if i%10==0 ]
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]
>>> l = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
>>> [ item for i,item in enumerate(l) if i%10==0 ]
['A', 'K', 'U']

Nhưng điều này vẫn chậm hơn nhiều so với việc chỉ sử dụng danh sách cắt.


-9

Danh sách hiểu được chính xác được thực hiện cho rằng:

smaller_list = [x for x in range(100001) if x % 10 == 0]

Bạn có thể nhận thêm thông tin về chúng trong tài liệu chính thức của python: http://docs.python.org/tutorial/datastructures.html#list-comprehensions


Giới hạn trên phải là 1000, không phải 10000. Giải pháp của bạn không bao gồm giới hạn trên 1000 do phạm vi dừng ở 999. +1 cho liên kết đến mức hiểu danh sách.

19
Điều này không thực sự rút ra mỗi mục thứ 10, nó lấy ra mọi mục có giá trị chia hết cho 10. Trong ví dụ cụ thể này là cùng một thứ, nhưng có thể không.
Andre Miller
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.