2009-07-20 77 views
40

Tôi có danh sách các danh sách hai mục và cần phải tìm kiếm những thứ trong đó.Tìm kiếm bằng Python trong danh sách các danh sách

Nếu danh sách là:

list =[ ['a','b'], ['a','c'], ['b','d'] ] 

Tôi có thể tìm kiếm một cặp dễ dàng bằng cách làm

['a','b'] in list 

Bây giờ, là có một cách để xem nếu tôi có một cặp, trong đó một chuỗi là hiện tại chỉ ở vị trí thứ hai? Tôi có thể làm điều này:

for i in range (0, len(list)): 
    if list[i][1]==search: 
     found=1 

Nhưng có cách nào (tốt hơn) không có vòng lặp for không? Tôi không cần biết i hoặc giữ vòng lặp sau khi tìm thấy.

Trả lời

31

Bạn sẽ luôn có vòng lặp - ai đó có thể đi kèm với một lớp lót thông minh che giấu vòng lặp trong một cuộc gọi tới map() hoặc tương tự, nhưng nó luôn ở đó.

Tùy chọn của tôi sẽ luôn có mã sạch và đơn giản, trừ khi hiệu suất là yếu tố chính.

Đây là có lẽ là một phiên bản Pythonic nhiều mã của bạn:

data = [['a','b'], ['a','c'], ['b','d']] 
search = 'c' 
for sublist in data: 
    if sublist[1] == search: 
     print "Found it!", sublist 
     break 
# Prints: Found it! ['a', 'c'] 

Nó phá vỡ ra khỏi vòng lặp ngay khi nó tìm thấy một trận đấu.

(Bạn có một lỗi đánh máy, bằng cách này, trong ['b''d'].)

+0

Tại thời điểm này trong "sự nghiệp" trăn của tôi, tôi ưu tiên phương pháp này vì nó rất dễ đọc. Tôi có thể trở lại để thử những người khác cho hiệu suất, mặc dù. Danh sách của tôi nhận được khá lớn tại một thời điểm. Có nơi nào tôi có thể so sánh hiệu suất của các cách tiếp cận khác nhau không? – greye

+1

Sử dụng mô-đun 'timeit' để kiểm tra hiệu suất của loại điều sau: http://docs.python.org/library/timeit.html – RichieHindle

+0

Điều gì xảy ra nếu chúng ta không biết liệu ký tự mà chúng ta đang tìm kiếm có trong vị trí thứ [0], [1] st, [2] ... trong danh sách lồng nhau? ví dụ. chúng tôi đang tìm kiếm 'b', bằng cách sử dụng phương pháp này, nó sẽ chỉ trả lại ['a', 'b'] thay vì ['b', 'd']. –

7
>>> my_list =[ ['a', 'b'], ['a', 'c'], ['b', 'd'] ] 
>>> 'd' in (x[1] for x in my_list) 
True 

Editing thêm:

Cả hai câu trả lời của David sử dụng bất kỳ và mỏ sử dụng trong sẽ kết thúc khi họ tìm một kết quả phù hợp vì chúng tôi đang sử dụng biểu thức trình tạo. Dưới đây là một thử nghiệm sử dụng một máy phát vô hạn để chứng minh rằng:

def mygen(): 
    ''' Infinite generator ''' 
    while True: 
     yield 'xxx' # Just to include a non-match in the generator 
     yield 'd' 

print 'd' in (x for x in mygen())  # True 
print any('d' == x for x in mygen()) # True 
# print 'q' in (x for x in mygen())  # Never ends if uncommented 
# print any('q' == x for x in mygen()) # Never ends if uncommented 

tôi giống như chỉ đơn giản là sử dụng trong thay vì cả hai ==bất kỳ.

+0

Đó là những gì nó phải làm. –

+0

Tôi nghĩ rằng bất kỳ() là rõ ràng hơn, nhưng tôi đoán nó chỉ là sở thích cá nhân. +1 sau đó ... –

53

Đây là cách Pythonic để làm điều đó:

data = [['a','b'], ['a','c'], ['b','d']] 
search = 'c' 
any(e[1] == search for e in data) 

Hoặc ... tốt, tôi sẽ không yêu cầu bồi thường này là "một trong những cách Pythonic true" để làm điều đó bởi vì tại một số điểm nó trở nên một chút chủ quan Pythonic là gì và những gì không phải là, hoặc phương pháp nào là Pythonic hơn một loại khác. Nhưng sử dụng any() chắc chắn là kiểu điển hình của Python hơn vòng lặp for như trong ví dụ: RichieHindle's answer,

Tất nhiên có một vòng lặp ẩn trong việc triển khai any, mặc dù nó thoát ra khỏi vòng lặp ngay sau khi tìm thấy kết quả phù hợp.


Vì tôi đã chán Tôi đã thực hiện một kịch bản thời gian để so sánh hiệu suất của các đề xuất khác nhau, sửa đổi một số trong số chúng khi cần thiết để làm cho API giống nhau.Bây giờ, chúng ta nên nhớ rằng nhanh nhất không phải luôn luôn tốt nhất, và được nhanh chóng chắc chắn không phải là điều tương tự như là Pythonic. Điều đó đang được nói, kết quả là ... lạ. Rõ ràng là các vòngrất nhanh, đó không phải là những gì tôi mong đợi, vì vậy tôi lấy những hạt này bằng một hạt muối mà không hiểu tại sao chúng lại xuất hiện theo cách của chúng.

Dù sao, khi tôi sử dụng danh sách được xác định trong câu hỏi với ba danh sách con của hai yếu tố từng, từ nhanh nhất để chậm nhất tôi nhận được những kết quả này:

  1. RichieHindle's answer với for vòng lặp, ghi nhận trong 0.22 ms
  2. Terence Honles' first suggestion mà tạo ra một danh sách, tại 0,36 ms
  3. Pierre-Luc Bedard's answer (last code block), tại 0,43 ms
  4. về cơ bản gắn giữa Markus's answerfor vòng từ the original question, tại 0,48 ms
  5. Coady's answer sử dụng operator.itemgetter(), tại 0,53 ms
  6. đủ Đóng để tính là một tie giữa Alex Martelli's answer với ifilter()Anon's answer, tại 0,67 ms (Alex luôn là khoảng nửa micro giây nhanh hơn)
  7. gần Một tie -enough giữa jojo's answer, mỏ, Brandon E Taylor's (mà giống hệt mỏ), và Terence Honles' second suggestion sử dụng any(), tất cả sắp tới trong lúc 0,81-0,82 ms
  8. và sau đó user27221's answer sử dụng comprehensions danh sách lồng nhau, tại 0,95 ms

Rõ ràng thời gian thực không có ý nghĩa đối với phần cứng của người khác, nhưng sự khác biệt giữa chúng nên đưa ra một số ý tưởng về các phương pháp khác nhau.

Khi tôi sử dụng danh sách dài hơn, mọi thứ sẽ thay đổi một chút. Tôi bắt đầu với danh sách trong câu hỏi, với ba danh sách con, và nối thêm 197 danh sách con khác, với tổng số 200 danh sách con mỗi chiều dài hai. Sử dụng danh sách dài này, đây là kết quả:

  1. RichieHindle's answer, đồng 0.22 ms như với danh sách ngắn
  2. Coady's answer sử dụng operator.itemgetter(), một lần nữa tại 0,53 ms
  3. Terence Honles' first suggestion mà tạo ra một danh sách, tại 0,36 ms
  4. một tie ảo giữa Alex Martelli's answer với ifilter()Anon's answer, tại 0,67 ms
  5. một lần nữa một tie cận đủ giữa câu trả lời của tôi, Brandon E Taylor's phương pháp giống hệt nhau, và Terence Honles' second suggestion sử dụng any(), tất cả sắp tới trong lúc 0,81-0,82 ms

Đó là những người mà giữ thời gian ban đầu của họ khi danh sách được mở rộng. Phần còn lại, mà không, là

  1. các for vòng từ the original question, tại 1,24 ms
  2. Terence Honles' first suggestion mà tạo ra một danh sách, tại 7.49 ms
  3. Pierre-Luc Bedard's answer (last code block), lúc 8 .12 ms
  4. Markus's answer, tại 10,27 ms
  5. jojo's answer, tại 19,87 ms
  6. Và cuối cùng user27221's answer sử dụng comprehensions danh sách lồng nhau, tại 60,59 ms
+0

ngắn và nhiều hơn nữa pythonic, vì vậy tôi thích nó ;-) –

+0

rất pythonesque – 3kstc

+0

Để so sánh các câu trả lời này xứng đáng là một "Cảm ơn bạn!" "spam" :) –

15
>>> the_list =[ ['a','b'], ['a','c'], ['b''d'] ] 
>>> any('c' == x[1] for x in the_list) 
True 
1
>>> the_list =[ ['a','b'], ['a','c'], ['b','d'] ] 
>>> "b" in zip(*the_list)[1] 
True 

zip() mất một loạt các danh sách và nhóm các phần tử lại với nhau theo chỉ mục, chuyển đổi hiệu quả danh sách ma trận danh sách. Dấu hoa thị có nội dung là the_list và gửi đến số zip làm đối số, vì vậy, bạn đang chuyển hiệu quả ba danh sách riêng biệt, đó là những gì mà zip muốn. Tất cả những gì còn lại là kiểm tra xem liệu "b" (hoặc bất kỳ thứ gì) có nằm trong danh sách được tạo thành từ các phần tử có chỉ mục mà bạn quan tâm.

4

Markus có một cách để tránh sử dụng từ for - đây là cách khác có hiệu suất tốt hơn nhiều trong thời gian dài the_list s ...:

import itertools 
found = any(itertools.ifilter(lambda x:x[1]=='b', the_list) 
+0

Ah, tốt, Alex đang ở đây. ;-) Rõ ràng, gen exp sử dụng từ 'for' - nhưng nếu chúng ta cho phép điều đó, diễn giải mục tiêu như tránh tiêu chuẩn cho cấu trúc vòng lặp thay vì từ 'for', thì tất cả các câu trả lời được so sánh như thế nào hiệu suất? – Anon

+0

@Anon, tôi không có thời gian để chạy các điều kiện thông thường (OSCON đang bật, và tôi khá bận rộn với nó ;-), nhưng từ kinh nghiệm trước đây tôi biết rằng itertools có xu hướng thực hiện như chớp sét. Tất cả các câu trả lời đều giúp Markus dừng lại ở trận đấu đầu tiên vì vậy tất cả chúng đều nhanh chóng theo nghĩa này. –

+0

NP. Cảm ơn. ;-) – Anon

2

Không có gì sai với việc sử dụng một exp gen, nhưng nếu mục tiêu là để nội tuyến vòng lặp ...

>>> import itertools, operator 
>>> 'b' in itertools.imap(operator.itemgetter(1), the_list) 
True 

nên là nhanh nhất cũng.

12

ở trên tất cả đều tốt đẹp

nhưng bạn có muốn giữ kết quả không?

nếu như vậy ...

bạn có thể sử dụng sau

result = [element for element in data if element[1] == search] 

sau đó một đơn giản

len(result) 

cho phép bạn biết nếu bất cứ điều gì đã được tìm thấy (và bây giờ bạn có thể làm mọi thứ với kết quả)

tất nhiên th không xử lý các phần tử có độ dài nhỏ hơn (bạn nên kiểm tra khi nào bạn không biết chúng luôn lớn hơn chiều dài 1 và trong trường hợp đó, bạn có đang sử dụng bộ đồ không? (Tuples là không thay đổi))

nếu bạn biết tất cả các mục là chiều dài bộ bạn cũng có thể làm:

any(second == search for _, second in data) 

hoặc cho len (dữ liệu [0]) == 4:

any(second == search for _, second, _, _ in data) 

...và tôi sẽ khuyên bạn sử dụng

for element in data: 
    ... 

thay vì

for i in range(len(data)): 
    ... 

(để sử dụng trong tương lai, trừ khi bạn muốn tiết kiệm hoặc sử dụng 'i', và chỉ để bạn biết các '0' là không cần thiết, bạn chỉ cần sử dụng cú pháp đầy đủ nếu bạn đang bắt đầu tại một giá trị phi zero)

+0

Cảm ơn! Tôi chỉ thích danh sách hiểu :)) !! – Mick

4

gì về:

list =[ ['a','b'], ['a','c'], ['b','d'] ] 
search = 'b' 

filter(lambda x:x[1]==search,list) 

Điều này sẽ trả về từng danh sách trong danh sách các danh sách có phần tử thứ hai bằng tìm kiếm.

-1

Đưa ra dưới đây là một cách đơn giản để tìm chính xác vị trí trong danh sách mục.

for i in range (0,len(a)): 
sublist=a[i] 
for i in range(0,len(sublist)): 
    if search==sublist[i]: 
     print "found in sublist "+ "a"+str(i) 
2

k bài cũ nhưng không có danh sách biểu một sử dụng để trả lời: P

list =[ ['a','b'], ['a','c'], ['b','d'] ] 
Search = 'c' 

# return if it find in either item 0 or item 1 
print [x for x,y in list if x == Search or y == Search] 

# return if it find in item 1 
print [x for x,y in list if y == Search] 
0

Tôi nghĩ rằng sử dụng comprehensions danh sách lồng nhau là cách thanh lịch nhất để giải quyết này, bởi vì các kết quả trung gian là vị trí nơi phần tử là. Triển khai sẽ là:

list =[ ['a','b'], ['a','c'], ['b','d'] ] 
search = 'c' 
any([ (list.index(x),x.index(y)) for x in list for y in x if y == search ]) 
Các vấn đề liên quan