Cách lấy phần tử thứ n của danh sách python hoặc mặc định nếu không có sẵn


143

Tôi đang tìm kiếm một tương đương trong python của dictionary.get(key, default)danh sách. Có một thành ngữ lót nào để lấy phần tử thứ n của danh sách hoặc giá trị mặc định nếu không có sẵn không?

Ví dụ, đưa ra một danh sách myList tôi muốn nhận myList[0], hoặc 5 nếu myListlà một danh sách trống.

Cảm ơn.

Câu trả lời:


123
l[index] if index < len(l) else default

Để hỗ trợ các chỉ số tiêu cực, chúng ta có thể sử dụng:

l[index] if -len(l) <= index < len(l) else default

3
Tuy nhiên, nó không hoạt động nếu danh sách của bạn không có tên - ví dụ, trong phần hiểu danh sách.
Xiong Chiamiov

9
Bạn dường như quên trường hợp chỉ số tiêu cực. Ví dụ. index == -1000000nên trở về default.
gật đầu

3
@nodakai, điểm tốt. Đôi khi tôi đã bị cắn bởi điều này. x[index] if 0 <= index < len(x) else defaultsẽ tốt hơn nếu indexcó thể là tiêu cực.
Ben Hoyt

3
@nodakai wow - đó là một ví dụ tuyệt vời về lý do tại sao có thể tốt hơn khi sử dụng thử / ngoại trừ, hơn là cố gắng viết mã chính xác cho bài kiểm tra để nó không bao giờ thất bại. Tôi vẫn không muốn dựa vào thử / ngoại trừ, đối với trường hợp mà tôi biết sẽ xảy ra, nhưng điều này làm tăng sự sẵn lòng của tôi để xem xét nó.
ToolmakerSteve 15/12/13

6
@ToolmakerSteve: Công thức của bạn valid_index()không chính xác. Các chỉ số tiêu cực hợp pháp trong Python - nó phải như vậy -len(l) <= index < len(l).
Tim Pietzcker

57
try:
   a = b[n]
except IndexError:
   a = default

Chỉnh sửa: Tôi đã xóa kiểm tra cho TypeError - có lẽ tốt hơn để cho người gọi xử lý việc này.


3
Tôi thích điều này. Chỉ cần bọc nó xung quanh một hàm để bạn có thể gọi nó với một lần lặp là một đối số. Dễ đọc hơn.
Noufal Ibrahim

35
(a[n:]+[default])[0]

Điều này có lẽ tốt hơn khi alớn hơn

(a[n:n+1]+[default])[0]

Điều này hoạt động bởi vì nếu a[n:]là một danh sách trống nếun => len(a)

Dưới đây là một ví dụ về cách làm việc này với range(5)

>>> range(5)[3:4]
[3]
>>> range(5)[4:5]
[4]
>>> range(5)[5:6]
[]
>>> range(5)[6:7]
[]

Và biểu hiện đầy đủ

>>> (range(5)[3:4]+[999])[0]
3
>>> (range(5)[4:5]+[999])[0]
4
>>> (range(5)[5:6]+[999])[0]
999
>>> (range(5)[6:7]+[999])[0]
999

5
Bạn sẽ viết điều này trong mã thực tế mà không có một bình luận để giải thích nó?
Peter Hansen

1
@Peter Hansen, chỉ khi tôi chơi golf;) Tuy nhiên, nó hoạt động trên tất cả các phiên bản Python. Câu trả lời được chấp nhận chỉ hoạt động trên 2.5+
John La Rooy

3
Mặc dù vậy, nó tạo ra 3 danh sách tạm thời và truy cập 2 chỉ mục để chọn một mục.
Joachim Jablon

Mặc dù tôi không bao giờ thực hiện điều này trong mã "thực", nhưng đây là một lớp lót ngắn gọn mà tôi có thể dễ dàng sử dụng trong REPL của python, vì vậy bạn có được upvote của tôi.
Cookyt

next(iter(lst[i:i+1]), default)- chỉ là một mục khác trong cuộc thi một lớp xấu xí khó hiểu.
jfs

28

Chỉ cần phát hiện ra rằng:

next(iter(myList), 5)

iter(l)trả về một iterator trên myList, next()sử dụng phần tử đầu tiên của iterator và gây ra StopIterationlỗi trừ khi được gọi với một giá trị mặc định, đó là trường hợp ở đây, đối số thứ hai,5

Điều này chỉ hoạt động khi bạn muốn phần tử thứ 1, đó là trường hợp trong ví dụ của bạn, nhưng không phải trong văn bản câu hỏi của bạn, vì vậy ...

Ngoài ra, nó không cần tạo danh sách tạm thời trong bộ nhớ và nó hoạt động cho bất kỳ loại lặp nào, ngay cả khi nó không có tên (xem nhận xét của Xiong Chiamiov về câu trả lời của gruszczy)


3
Kết hợp với các câu trả lời khác: next(iter(myList[n:n+1]), 5) Bây giờ nó hoạt động cho nphần tử thứ.
Alfe

Điều đó sẽ không làm việc với danh sách không. Tại thời điểm này, hãy thử: ngoại trừ một IndexError đọc như một ý tưởng tốt hơn IMHO. Ngoài ra, nó tạo ra một danh sách trong bộ nhớ.
Joachim Jablon

Các trybiến thể không phải là một-liner (ask hỏi cho bằng OP). Nó chỉ hoạt động cho các danh sách vì myListlà một danh sách theo quy định của OP (nói chính xác, nó là một cái gì đó có thể lập chỉ mục). Tạo một bản sao trong bộ nhớ không tốn kém ở đây vì tôi đang tạo một danh sách một yếu tố duy nhất (hoặc không có) ở đây. Chắc chắn, một chút chi phí, nhưng không đáng để đề cập trừ khi bạn làm điều đó hàng triệu lần trong một vòng lặp. Btw, tôi đoán việc tạo và bắt một IndexErrorngoại lệ có lẽ tốn kém hơn.
Alfe

số lượng khả năng đọc :) Nếu bạn đang ở thời điểm nâng IndexError có chi phí đáng kể, có lẽ bạn nên làm toàn bộ trong Python.
Joachim Jablon

20
(L[n:n+1] or [somedefault])[0]

1
+1 vì điều này khiến tôi đi tìm hiểu những gì [] or ...đã làm. Tuy nhiên, cá nhân tôi chỉ sử dụng giải pháp được chấp nhận, vì nó dễ đọc (đối với người mới). Được cấp, gói nó trong một 'def' với bình luận sẽ làm cho điều đó phần lớn không thành vấn đề.
ToolmakerSteve 15/12/13

Điều gì nếu L[n] == Falsehoặc L[n] == Nonehoặc L[n] == []nhiều hơn trên toàn cầu bất cứ điều gì đánh giá là Sai?
Joachim Jablon

@JoachimJablon: Vẫn hoạt động. Các lát trả về một danh sách, và [False]là đúng.
Ignacio Vazquez-Abrams

Ồ, không nhận ra danh sách đã được kiểm tra và thực sự không phải là giá trị.
Joachim Jablon

Tại sao bạn bọc cái này trong một tuple? Tôi nghĩ rằng myval = l[n:n+1] or [somedefault]sẽ làm việc tốt?
Rotareti

8

... đang tìm kiếm một tương đương trong python của dict.get(key, default)danh sách

Có một công thức itertools thực hiện điều này cho các lần lặp chung. Để thuận tiện, bạn có thể > pip install more_itertoolsvà nhập thư viện bên thứ ba này thực hiện các công thức nấu ăn đó cho bạn:

import more_itertools as mit


mit.nth([1, 2, 3], 0)
# 1    

mit.nth([], 0, 5)
# 5    

Chi tiết

Dưới đây là việc thực hiện các nthcông thức:

def nth(iterable, n, default=None):
    "Returns the nth item or a default value"
    return next(itertools.islice(iterable, n, None), default)

Giống như dict.get(), công cụ này trả về một mặc định cho các chỉ số bị thiếu. Nó áp dụng cho các lần lặp chung:

mit.nth((0, 1, 2), 1)                                      # tuple
# 1

mit.nth(range(3), 1)                                       # range generator (py3)
# 1

mit.nth(iter([0, 1, 2]), 1)                                # list iterator 
# 1  

2

Một giải pháp rẻ tiền là thực sự tạo ra một lệnh với liệt kê và sử dụng .get()như bình thường, như

 dict(enumerate(l)).get(7, my_default)

1

Kết hợp @ Joachim với những điều trên, bạn có thể sử dụng

next(iter(my_list[index:]), default)

Ví dụ:

next(iter(range(10)[8:]), 11)
8
>>> next(iter(range(10)[12:]), 11)
11

Hoặc, có thể rõ ràng hơn, nhưng không có len

my_list[index] if my_list[index:] else default

1

Sử dụng Python 3.4 contextlib.suppress(exceptions)để xây dựng một getitem()phương thức tương tự getattr().

import contextlib

def getitem(iterable, index, default=None):
    """Return iterable[index] or default if IndexError is raised."""
    with contextlib.suppress(IndexError):
        return iterable[index]
    return default
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.