Python: ค้นหาในรายการ


585

ฉันเจอสิ่งนี้แล้ว:

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

แต่บางครั้งมันไม่ทำงานกับรายการทั้งหมดของฉันราวกับว่าพวกเขาไม่ได้รับการยอมรับในรายการ (เมื่อมันเป็นรายการของสตริง)

นี่เป็นวิธี 'pythonic' ที่มากที่สุดในการค้นหารายการในรายการif x in l:หรือไม่?


3
myListนั่นเป็นที่ดีที่สุดที่ดีและควรจะทำงานถ้ารายการเท่ากับหนึ่งขององค์ประกอบภายใน
Niklas B.

1
คุณหมายถึงเป็นวิธีที่ดีในการทำสิ่งต่าง ๆ ? ในการทดลองหลายครั้งของฉันอาจมีช่องว่างและการป้อนบรรทัดรบกวน ... ฉันแค่ต้องการให้แน่ใจว่ามันเป็นวิธีที่ดีในการใช้ "ค้นหาในรายการ" (โดยทั่วไป)
Stephane Rolland

คำตอบ:


1173

สำหรับคำถามแรกของคุณ: รหัสที่เป็นอย่างดีที่ดีและควรจะทำงานถ้าเท่ากับหนึ่งขององค์ประกอบภายในitem myListบางทีคุณอาจลองค้นหาสตริงที่ไม่ตรงกับรายการใดรายการหนึ่งหรือบางทีคุณกำลังใช้ค่าทศนิยมซึ่งทนทุกข์จากความไม่ถูกต้อง

สำหรับคำถามที่สองของคุณ: มีหลายวิธีที่เป็นไปได้ถ้า "ค้นหา" สิ่งต่าง ๆ ในรายการ

ตรวจสอบว่ามีบางสิ่งอยู่ภายในหรือไม่

นี่คือกรณีการใช้งานที่คุณอธิบาย: การตรวจสอบว่ามีบางอย่างอยู่ในรายการหรือไม่ อย่างที่คุณทราบคุณสามารถใช้inโอเปอเรเตอร์เพื่อ:

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

กรองคอลเลกชัน

นั่นคือการค้นหาองค์ประกอบทั้งหมดในลำดับที่ตรงตามเงื่อนไข คุณสามารถใช้ list comprehension หรือ expression generator สำหรับสิ่งต่อไปนี้

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

อันหลังจะส่งคืนเครื่องกำเนิดไฟฟ้าซึ่งคุณสามารถจินตนาการได้ว่าเป็นรายการสันหลังยาวที่จะสร้างขึ้นทันทีที่คุณทำซ้ำ โดยวิธีการแรกที่เทียบเท่ากับ

matches = filter(fulfills_some_condition, lst)

ใน Python 2 ที่นี่คุณสามารถดูฟังก์ชั่นการสั่งซื้อที่สูงขึ้นได้ ใน Python 3 filterจะไม่ส่งคืนรายการ แต่เป็นวัตถุที่เหมือนตัวสร้าง

การค้นหาสิ่งที่เกิดขึ้นครั้งแรก

หากคุณต้องการเพียงสิ่งแรกที่ตรงกับเงื่อนไข (แต่คุณไม่ทราบว่ามันคืออะไร) ก็เป็นเรื่องดีที่จะใช้สำหรับวง (อาจใช้elseประโยคเช่นกันซึ่งไม่เป็นที่รู้จักกันดี) คุณยังสามารถใช้

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

ซึ่งจะส่งคืนการแข่งขันครั้งแรกหรือเพิ่มStopIterationหากไม่พบ หรือคุณสามารถใช้

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

การค้นหาตำแหน่งของรายการ

สำหรับรายการนอกจากนี้ยังมีindexวิธีการที่บางครั้งอาจเป็นประโยชน์ถ้าคุณต้องการที่จะรู้ว่าที่เป็นองค์ประกอบบางอย่างในรายการ:

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

อย่างไรก็ตามโปรดทราบว่าหากคุณมีข้อมูลซ้ำให้.indexส่งคืนดัชนีต่ำสุด: ......

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

หากมีการซ้ำซ้อนและคุณต้องการดัชนีทั้งหมดคุณสามารถใช้enumerate()แทน:

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

10
สเตฟาน: ผมขอใช้ถ้อยคำมันif x in listเป็นไม่ได้สิ่งที่คนบ่นไม่เป็นฟังก์ชั่น พวกเขาบ่นเกี่ยวกับความจริงที่ว่าไม่มีวิธีที่ชัดเจนในการค้นหาสิ่งที่เกิดขึ้นครั้งแรกในรายการที่ตรงกับเงื่อนไขที่แน่นอน แต่ตามที่ระบุไว้ในคำตอบของฉันnext()สามารถใช้ (ab) สำหรับสิ่งนั้นได้
Niklas B.

3
@Stephane: อันที่สองไม่ได้สร้าง tuple แต่เป็นตัวกำเนิด (ซึ่งเป็นรายการที่ยังไม่ได้สร้างโดยทั่วไป) หากคุณต้องการใช้ผลลัพธ์เพียงครั้งเดียวเครื่องกำเนิดไฟฟ้ามักจะพอใจมากกว่า อย่างไรก็ตามหากคุณต้องการใช้คอลเลกชันที่สร้างขึ้นหลายครั้งหลังจากนั้นขอแนะนำให้สร้างรายการที่ชัดเจนตั้งแต่แรก ลองดูการอัปเดตของฉันตอนนี้มันมีโครงสร้างที่ดีขึ้นนิดหน่อย :)
Niklas B.

26
ตัวอย่าง "การค้นหาสิ่งที่เกิดขึ้นครั้งแรก" ของคุณคือสีทอง รู้สึก[list comprehension...][0]
ไพทอน

4
ฉันผิดหวังมากขึ้นด้วยความสามารถในการใช้งานของงูหลาม ใน haskell มีฟังก์ชั่นการค้นหาในโมดูล Data.List ที่ทำอย่างนั้น แต่ในไพ ธ อนมันไม่ได้และมันเล็กไปสำหรับการเป็นไลบรารี่ดังนั้นคุณต้องปรับใช้ตรรกะเดียวกันซ้ำแล้วซ้ำอีก ช่างเป็นการเสียเปล่า ...
user1685095

3
มันจะดีถ้ามี kwarg ไปindex()เรียกว่าkeyที่ทำงานเช่นkeyการยอมรับจากmax(); ตัวอย่างเช่น: index(list, key=is_prime).
Curt

189

หากคุณต้องการค้นหาองค์ประกอบหนึ่งหรือNoneใช้ค่าเริ่มต้นในnextมันจะไม่เพิ่มขึ้นStopIterationหากรายการไม่พบในรายการ:

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

1
nextใช้ตัววนซ้ำเป็นพารามิเตอร์ตัวแรกและ list / tuple ไม่ใช่ตัววนซ้ำ ดังนั้นควรfirst_or_default = next(iter([x for x in lst if ...]), None)ดูdocs.python.org/3/library/functions.html#next
Devy

7
@Devy: ถูกต้อง แต่(x for x in lst if ...)เป็นตัวกำเนิดของรายการlst(ซึ่งเป็นตัววนซ้ำ) ถ้าคุณทำnext(iter([x for x in lst if ...]), None)คุณจะต้องสร้างรายการ[x for x in lst if ...]ซึ่งจะเป็นการดำเนินการที่แพงกว่ามาก
Erlend Graff

1
มีนามธรรมที่นี่เพื่อกำหนดฟังก์ชั่นการค้นหา เพียงแค็ปซูลการขยายบูลีนของifแลมบ์ดา & คุณสามารถเขียนได้ตามfind(fn,list)ปกติแทนที่จะเป็นรหัสกำเนิดที่ทำให้งง
Semi Marant

22

ในขณะที่คำตอบจาก Niklas B. นั้นค่อนข้างครอบคลุมเมื่อเราต้องการค้นหารายการในรายการบางครั้งมันก็มีประโยชน์ที่จะได้รับดัชนี:

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

11

การค้นหาสิ่งที่เกิดขึ้นครั้งแรก

มีสูตรสำหรับสิ่งนั้นใน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)

ตัวอย่างเช่นรหัสต่อไปนี้จะค้นหาหมายเลขคี่แรกในรายการ:

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

6

อีกทางเลือกหนึ่ง: คุณสามารถตรวจสอบว่ารายการนั้นอยู่ในรายการด้วยif item in list:หรือไม่ แต่นี่เป็นลำดับ O (n) หากคุณกำลังจัดการกับรายการขนาดใหญ่และสิ่งที่คุณต้องรู้คือสิ่งที่เป็นสมาชิกของรายการคุณสามารถแปลงรายการเป็นชุดแรกและใช้ประโยชน์จากการค้นหาชุดเวลาที่แน่นอน :

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

ไม่ใช่วิธีแก้ปัญหาที่ถูกต้องในทุกกรณี แต่สำหรับบางกรณีนี่อาจให้ประสิทธิภาพที่ดีขึ้น

โปรดทราบว่าการสร้างชุดด้วยset(my_list)นั้นยังเป็น O (n) ดังนั้นหากคุณต้องการทำสิ่งนี้เพียงครั้งเดียวก็จะไม่เร็วไปกว่าที่จะทำเช่นนี้ หากคุณต้องการตรวจสอบการเป็นสมาชิกซ้ำ ๆ สิ่งนี้จะเป็น O (1) สำหรับการค้นหาทุกครั้งหลังจากการสร้างชุดเริ่มต้นนั้น


4

คุณอาจต้องการใช้หนึ่งในสองการค้นหาที่เป็นไปได้ในขณะที่ทำงานกับรายการของสตริง:

  1. หากองค์ประกอบรายการเท่ากับรายการ ('ตัวอย่าง' อยู่ใน ['หนึ่ง', 'ตัวอย่าง', 'สอง']):

    if item in your_list: some_function_on_true()

    'ex' ใน ['one', 'ex', 'two'] => จริง

    'ex_1' ใน ['หนึ่ง', 'อดีต', 'สอง'] => เท็จ

  2. หากองค์ประกอบของรายการเหมือนกับรายการ ('ex' อยู่ใน ['one' 'example', 'two'] หรือ 'example_1' อยู่ใน ['one', 'example', 'two']):

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

    หรือ

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

    จากนั้นเพียงตรวจสอบlen(matches)หรืออ่านหากจำเป็น


3

ความหมายและการใช้งาน

count()วิธีการส่งกลับจำนวนขององค์ประกอบที่มีค่าที่ระบุ

วากยสัมพันธ์

list.count(value)

ตัวอย่าง:

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

x = fruits.count("cherry")

ตัวอย่างคำถาม:

item = someSortOfSelection()

if myList.count(item) >= 1 :

    doMySpecialFunction(item)

2
รายการนี้มีประสิทธิภาพมากหรือไม่? พูดถึงรายการหนึ่งล้านหรือไม่
3kstc

1
ฉันไม่แน่ใจ !!!
josef

1

แทนที่จะใช้list.index(x)ซึ่งคืนค่าดัชนีของ x หากพบในรายการหรือส่งคืน#ValueErrorข้อความหากไม่พบ x คุณสามารถใช้list.count(x)ซึ่งคืนค่าจำนวนการเกิดขึ้นของ x ในรายการ (การตรวจสอบความถูกต้องว่า x เป็นจริงในรายการ) หรือ ส่งกลับ 0 มิฉะนั้น (ในกรณีที่ไม่มี x) สิ่งที่ยอดเยี่ยมเกี่ยวกับการcount()ที่มันไม่ทำลายรหัสของคุณหรือต้องการให้คุณยกเว้นสำหรับเมื่อไม่พบ x


และสิ่งเลวร้ายก็คือมันนับองค์ประกอบ ไม่หยุดเมื่อพบองค์ประกอบ ดังนั้นประสิทธิภาพจึงไม่ดีสำหรับรายชื่อที่ยิ่งใหญ่
Jean-François Fabre

1

หากคุณกำลังจะตรวจสอบว่ามีค่าอยู่ในของสะสมหนึ่งครั้งแล้วใช้ 'ใน' ผู้ประกอบการเป็นเรื่องปกติ อย่างไรก็ตามถ้าคุณจะตรวจสอบมากกว่าหนึ่งครั้งฉันแนะนำให้ใช้โมดูล bisect โปรดทราบว่าการใช้ข้อมูลโมดูล bisect จะต้องเรียงลำดับ ดังนั้นคุณจึงจัดเรียงข้อมูลเพียงครั้งเดียวจากนั้นคุณสามารถใช้เส้นแบ่งครึ่ง การใช้โมดูล bisect บนเครื่องของฉันเร็วกว่าการใช้ตัวดำเนินการ 'in' ประมาณ 12 เท่า

นี่คือตัวอย่างของรหัสโดยใช้ Python 3.8 และสูงกว่าไวยากรณ์:

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}")

เอาท์พุท:

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

ตรวจสอบว่าไม่มีช่องว่างสีขาวเพิ่มเติม / ไม่ต้องการในรายการของรายการสตริง นั่นเป็นเหตุผลที่สามารถขัดขวางการอธิบายรายการที่ไม่สามารถพบได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.