Python: Tìm trong danh sách


586

Tôi đã đi qua điều này:

item = someSortOfSelection()
if item in myList:
    doMySpecialFunction(item)

nhưng đôi khi nó không hoạt động với tất cả các mục của tôi, như thể chúng không được nhận dạng trong danh sách (khi đó là danh sách chuỗi).

Đây có phải là cách 'pythonic' nhất để tìm một mục trong danh sách : if x in l:?


3
Điều đó hoàn toàn tốt và sẽ hoạt động nếu vật phẩm bằng một trong các yếu tố bên trong myList.
Niklas B.

1
bạn có nghĩa đó là cách tốt để làm việc? trong một số thử nghiệm của tôi, có thể có các khoảng trắng và các nguồn cấp dữ liệu xen kẽ ... tôi chỉ muốn chắc chắn rằng đó là cách tốt để thực hiện "tìm trong danh sách" (nói chung)
Stephane Rolland

Câu trả lời:


1174

Đối với câu hỏi đầu tiên của bạn: mã đó hoàn toàn tốt và sẽ hoạt động nếu itembằng một trong các yếu tố bên trong myList. Có thể bạn cố gắng tìm một chuỗi không khớp chính xác với một trong các mục hoặc có thể bạn đang sử dụng một giá trị float bị thiếu chính xác.

Đối với câu hỏi thứ hai của bạn: Thực sự có một số cách có thể nếu "tìm" những thứ trong danh sách.

Kiểm tra nếu có cái gì đó bên trong

Đây là trường hợp sử dụng mà bạn mô tả: Kiểm tra xem có thứ gì đó nằm trong danh sách hay không. Như bạn biết, bạn có thể sử dụng intoán tử cho điều đó:

3 in [1, 2, 3] # => True

Lọc một bộ sưu tập

Đó là, tìm tất cả các yếu tố trong một chuỗi đáp ứng một điều kiện nhất định. Bạn có thể sử dụng hiểu danh sách hoặc biểu thức trình tạo cho điều đó:

matches = [x for x in lst if fulfills_some_condition(x)]
matches = (x for x in lst if x > 6)

Cái sau sẽ trả về một trình tạo mà bạn có thể tưởng tượng như một loại danh sách lười biếng sẽ chỉ được xây dựng ngay khi bạn lặp qua nó. Nhân tiện, cái đầu tiên chính xác tương đương với

matches = filter(fulfills_some_condition, lst)

trong Python 2. Ở đây bạn có thể thấy các hàm bậc cao hơn tại nơi làm việc. Trong Python 3, filterkhông trả về một danh sách, mà là một đối tượng giống như trình tạo.

Tìm sự xuất hiện đầu tiên

Nếu bạn chỉ muốn điều đầu tiên phù hợp với một điều kiện (nhưng bạn không biết nó là gì), thì tốt nhất là sử dụng vòng lặp for (có thể sử dụng elsemệnh đề này, điều này không thực sự nổi tiếng). Bạn cũng có thể dùng

next(x for x in lst if ...)

sẽ trả lại trận đấu đầu tiên hoặc tăng StopIterationnếu không tìm thấy. Ngoài ra, bạn có thể sử dụng

next((x for x in lst if ...), [default value])

Tìm vị trí của một mặt hàng

Đối với danh sách, cũng có indexphương pháp đôi khi có thể hữu ích nếu bạn muốn biết vị trí của một yếu tố nào đó trong danh sách:

[1,2,3].index(2) # => 1
[1,2,3].index(4) # => ValueError

Tuy nhiên, lưu ý rằng nếu bạn có các bản sao, .indexluôn trả về chỉ số thấp nhất: ......

[1,2,3,2].index(2) # => 1

Nếu có trùng lặp và bạn muốn tất cả các chỉ mục thì bạn có thể sử dụng enumerate()thay thế:

[i for i,x in enumerate([1,2,3,2]) if x==2] # => [1, 3]

10
Stephane: Hãy để tôi nói lại nó: if x in listkhông những điều mà mọi người phàn nàn không phải là một chức năng built-in. Họ phàn nàn về thực tế rằng không có cách rõ ràng để tìm sự xuất hiện đầu tiên của một cái gì đó trong danh sách phù hợp với một điều kiện nhất định. Nhưng như đã nêu trong câu trả lời của tôi, next()có thể (ab) được sử dụng cho điều đó.
Niklas B.

3
@Stephane: Cái thứ hai không tạo ra một tuple, mà là một trình tạo (về cơ bản là một danh sách chưa được xây dựng). Nếu bạn muốn sử dụng kết quả chỉ một lần, máy phát điện thường thích hợp hơn. Tuy nhiên, nếu bạn muốn sử dụng bộ sưu tập đã tạo nhiều lần sau đó, bạn nên tạo một danh sách rõ ràng ở vị trí đầu tiên. Hãy xem bản cập nhật của tôi, bây giờ nó có cấu trúc tốt hơn một chút :)
Niklas B.

26
Ví dụ "tìm sự xuất hiện đầu tiên" của bạn là vàng. Cảm thấy nhiều pythonic hơn so với [list comprehension...][0]cách tiếp cận
acjay

4
Tôi càng ngày càng không đồng tình với khả năng 'chức năng' của python. Trong haskell có chức năng tìm trong mô-đun Data.List thực hiện chính xác điều đó. Nhưng trong python, nó không phải là nhỏ và biến nó thành một thư viện để bạn phải thực hiện lại cùng một logic nhiều lần. Thật là lãng phí ...
user1685095

3
Sẽ thật tuyệt nếu có một kwarg index()được gọi là keyhoạt động như keyđược chấp nhận bởi max(); ví dụ : index(list, key=is_prime).
Curt

189

Nếu bạn muốn tìm một yếu tố hoặc Nonesử dụng mặc định next, nó sẽ không tăng StopIterationnếu mục đó không được tìm thấy trong danh sách:

first_or_default = next((x for x in lst if ...), None)

1
nextlấy một iterator làm tham số đầu tiên và một danh sách / tuple KHÔNG phải là một iterator. Vì vậy, nên first_or_default = next(iter([x for x in lst if ...]), None)xem docs.python.org/3/l Library / fiances.html
Devy

7
@Devy: đúng vậy, nhưng (x for x in lst if ...)là một trình tạo trong danh sách lst( trình lặp). Nếu bạn làm như vậy next(iter([x for x in lst if ...]), None), bạn phải xây dựng danh sách [x for x in lst if ...], đó sẽ là một hoạt động tốn kém hơn nhiều.
Erlend Graff

1
Có một sự trừu tượng ở đây để xác định hàm tìm. Chỉ cần gói gọn việc hết hạn boolean iftrong lambda & bạn có thể viết find(fn,list)thường thay vì làm xáo trộn mã trình tạo.
bán kết

22

Mặc dù câu trả lời từ Niklas B. khá toàn diện, nhưng khi chúng tôi muốn tìm một mục trong danh sách, đôi khi rất hữu ích để có được chỉ mục của nó:

next((i for i, x in enumerate(lst) if [condition on x]), [default value])

11

Tìm sự xuất hiện đầu tiên

Có một công thức cho điều đó trong itertools:

def first_true(iterable, default=False, pred=None):
    """Returns the first true value in the iterable.

    If no true value is found, returns *default*

    If *pred* is not None, returns the first item
    for which pred(item) is true.

    """
    # first_true([a,b,c], x) --> a or b or c or x
    # first_true([a,b], x, f) --> a if f(a) else b if f(b) else x
    return next(filter(pred, iterable), default)

Ví dụ: đoạn mã sau tìm thấy số lẻ đầu tiên trong danh sách:

>>> first_true([2,3,4,5], None, lambda x: x%2==1)
3  

6

Một cách khác: bạn có thể kiểm tra xem một mục có trong danh sách hay không if item in list:, nhưng đây là thứ tự O (n). Nếu bạn đang xử lý danh sách lớn các mặt hàng và tất cả những gì bạn cần biết là liệu thứ gì đó có phải là thành viên trong danh sách của bạn hay không, bạn có thể chuyển đổi danh sách thành bộ đầu tiên và tận dụng thời gian tra cứu liên tục :

my_set = set(my_list)
if item in my_set:  # much faster on average than using a list
    # do something

Sẽ không phải là giải pháp chính xác trong mọi trường hợp, nhưng đối với một số trường hợp, điều này có thể mang lại cho bạn hiệu suất tốt hơn.

Lưu ý rằng việc tạo tập hợp set(my_list)cũng là O (n), vì vậy nếu bạn chỉ cần thực hiện việc này một lần thì sẽ không nhanh hơn để làm theo cách này. Nếu bạn cần liên tục kiểm tra tư cách thành viên, thì đây sẽ là O (1) cho mỗi lần tra cứu sau khi tạo bộ ban đầu.


4

Bạn có thể muốn sử dụng một trong hai tìm kiếm có thể trong khi làm việc với danh sách các chuỗi:

  1. nếu phần tử danh sách bằng với một mục ('ví dụ' nằm trong ['một', 'ví dụ', 'hai']):

    if item in your_list: some_function_on_true()

    'ex' trong ['một', 'ex', 'hai'] => Đúng

    'ex_1' trong ['một', 'ex', 'hai'] => Sai

  2. nếu phần tử danh sách giống như một mục ('ex' nằm trong ['một,' ví dụ ',' hai '] hoặc' example_1 'nằm trong [' một ',' ví dụ ',' hai ']):

    matches = [el for el in your_list if item in el]

    hoặc là

    matches = [el for el in your_list if el in item]

    sau đó chỉ cần kiểm tra len(matches)hoặc đọc chúng nếu cần.


3

Định nghĩa và cách sử dụng

các count()phương thức trả về số phần tử với giá trị quy định.

Cú pháp

list.count(value)

thí dụ:

fruits = ['apple', 'banana', 'cherry']

x = fruits.count("cherry")

Ví dụ câu hỏi:

item = someSortOfSelection()

if myList.count(item) >= 1 :

    doMySpecialFunction(item)

2
Đây có phải là hiệu quả trong một danh sách rất dài? Nói danh sách một triệu?
3kstc

1
Tôi không chắc !!!
josef

1

Thay vì sử dụng list.index(x)trả về chỉ mục của x nếu nó được tìm thấy trong danh sách hoặc trả về một #ValueErrorthông báo nếu không tìm thấy x, bạn có thể sử dụng list.count(x)trả về số lần xuất hiện của x trong danh sách (xác thực rằng x thực sự có trong danh sách) hoặc nó trả về 0 nếu không (trong trường hợp không có x). Điều thú vị count()là nó không phá vỡ mã của bạn hoặc yêu cầu bạn ném ngoại lệ khi không tìm thấy x


và điều tồi tệ là nó tính các yếu tố. Nó không dừng lại khi yếu tố được tìm thấy. vì vậy hiệu suất rất tệ trong danh sách lớn
Jean-François Fabre

1

Nếu bạn định kiểm tra xem giá trị có tồn tại trong bộ sưu tập một lần hay không thì sử dụng toán tử 'in' có ổn không. Tuy nhiên, nếu bạn định kiểm tra nhiều lần thì tôi khuyên bạn nên sử dụng mô-đun bisect. Hãy nhớ rằng sử dụng dữ liệu mô-đun bisect phải được sắp xếp. Vì vậy, bạn sắp xếp dữ liệu một lần và sau đó bạn có thể sử dụng bisect. Sử dụng mô-đun bisect trên máy của tôi nhanh hơn khoảng 12 lần so với sử dụng toán tử 'in'.

Dưới đây là một ví dụ về mã sử dụng cú pháp Python 3.8 trở lên:

import bisect
from timeit import timeit

def bisect_search(container, value):
    return (
      (index := bisect.bisect_left(container, value)) < len(container) 
      and container[index] == value
    )

data = list(range(1000))
# value to search
true_value = 666
false_value = 66666

# times to test
ttt = 1000

print(f"{bisect_search(data, true_value)=} {bisect_search(data, false_value)=}")

t1 = timeit(lambda: true_value in data, number=ttt)
t2 = timeit(lambda: bisect_search(data, true_value), number=ttt)

print("Performance:", f"{t1=:.4f}, {t2=:.4f}, diffs {t1/t2=:.2f}")

Đầu ra:

bisect_search(data, true_value)=True bisect_search(data, false_value)=False
Performance: t1=0.0220, t2=0.0019, diffs t1/t2=11.71

0

Kiểm tra không có khoảng trắng bổ sung / không mong muốn trong các mục của danh sách các chuỗi. Đó là một lý do có thể can thiệp vào việc giải thích các mục không thể được tìm thấy.

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.