ค้นหาวัตถุในรายการที่มีคุณลักษณะเท่ากับค่าบางอย่าง (ที่ตรงตามเงื่อนไขใด ๆ )


221

ฉันมีรายการสิ่งของ ฉันต้องการที่จะหาคน (ครั้งแรกหรืออะไรก็ตาม) วัตถุในรายการนี้ที่มีแอตทริบิวต์ (หรือผลวิธีการ - อะไรก็ตาม) valueเท่ากับ

วิธีที่ดีที่สุดในการค้นหาคืออะไร

นี่คือกรณีทดสอบ:

  class Test:
      def __init__(self, value):
          self.value = value

  import random

  value = 5

  test_list = [Test(random.randint(0,100)) for x in range(1000)]

  # that I would do in Pascal, I don't believe isn't anywhere near 'Pythonic'
  for x in test_list:
      if x.value == value:
          print "i found it!"
          break

ฉันคิดว่าการใช้เครื่องกำเนิดไฟฟ้าและreduce()จะไม่สร้างความแตกต่างใด ๆ เพราะมันจะยังคงวนซ้ำผ่านรายการ

ปล.: สมการvalueเป็นเพียงตัวอย่าง แน่นอนว่าเราต้องการองค์ประกอบที่ตรงตามเงื่อนไข


2
นี่คือการอภิปรายที่ดีของคำถามนี้: tomayko.com/writings/cleanest-python-find-in-list-function
Andrew Hare

โพสต์ต้นฉบับเป็นขันออกจากวัน แต่การตอบสนองที่ 2 ตรงกับรุ่นหนึ่งบรรทัดของฉันว่า ฉันไม่เชื่อว่ามันจะดีกว่ารุ่นลูปพื้นฐาน
agf

คำตอบ:


433
next((x for x in test_list if x.value == value), None)

สิ่งนี้จะได้รับไอเท็มแรกจากรายการที่ตรงกับเงื่อนไขและส่งคืนNoneหากไม่มีรายการที่ตรงกัน เป็นรูปแบบนิพจน์เดียวที่ฉันต้องการ

อย่างไรก็ตาม

for x in test_list:
    if x.value == value:
        print "i found it!"
        break

รุ่นลูปทำลายที่ไร้เดียงสาเป็น Pythonic อย่างสมบูรณ์แบบ - มันกระชับชัดเจนและมีประสิทธิภาพ วิธีทำให้มันเข้ากับพฤติกรรมของสายการบินเดียว:

for x in test_list:
    if x.value == value:
        print "i found it!"
        break
else:
    x = None

สิ่งนี้จะกำหนดNoneให้xถ้าคุณไม่breakออกจากวง


72
+1 สำหรับความมั่นใจ "รุ่นลูปทำลายที่ไร้เดียงสาเป็น Pythonic ที่สมบูรณ์แบบ"
LaundroMat

ทางออกที่ดี แต่ฉันจะแก้ไขบรรทัดของคุณเพื่อให้ฉันสามารถทำให้ x.value จริงหมายถึง x.fieldMemberName ที่ชื่อนั้นถูกเก็บไว้ในค่า? ฟิลด์ = "ชื่อ" ถัดไป ((x สำหรับ x ใน test_list ถ้า x.field == ค่า), ไม่มี) ดังนั้นในกรณีนี้ฉันกำลังตรวจสอบกับ x.name ไม่ใช่ x.field
Stewart Dale

3
@StewartDale มันไม่ได้โดยสิ้นเชิงล้างสิ่งที่คุณขอ ... if getattr(x, x.fieldMemberName) == valueแต่ฉันคิดว่าคุณหมายถึง ที่จะดึงข้อมูลจากแอตทริบิวต์xที่มีชื่อที่จัดเก็บในและเปรียบเทียบกับfieldMemberName value
agf

1
@ThatTechGuy - The elseประโยคที่มีความหมายที่จะเป็นห่วงไม่ได้for if(ปฏิเสธการแก้ไข)
agf

1
@agf ว้าวฉันไม่รู้จริงๆว่ามีอยู่จริง .. book.pythontips.com/th/latest/for_-_else.htmlเยี่ยมมาก!
ThatTechGuy

25

เนื่องจากยังไม่ได้รับการกล่าวถึงเพียงเพื่อความสำเร็จ ตัวกรอง ol ที่ดีเพื่อกรององค์ประกอบการกรองของคุณ

การเขียนโปรแกรมการทำงาน ftw

####### Set Up #######
class X:

    def __init__(self, val):
        self.val = val

elem = 5

my_unfiltered_list = [X(1), X(2), X(3), X(4), X(5), X(5), X(6)]

####### Set Up #######

### Filter one liner ### filter(lambda x: condition(x), some_list)
my_filter_iter = filter(lambda x: x.val == elem, my_unfiltered_list)
### Returns a flippin' iterator at least in Python 3.5 and that's what I'm on

print(next(my_filter_iter).val)
print(next(my_filter_iter).val)
print(next(my_filter_iter).val)

### [1, 2, 3, 4, 5, 5, 6] Will Return: ###
# 5
# 5
# Traceback (most recent call last):
#   File "C:\Users\mousavin\workspace\Scripts\test.py", line 22, in <module>
#     print(next(my_filter_iter).value)
# StopIteration


# You can do that None stuff or whatever at this point, if you don't like exceptions.

ฉันรู้ว่าโดยทั่วไปในรายการความเข้าใจหลามเป็นที่ต้องการหรืออย่างน้อยนั่นคือสิ่งที่ฉันอ่าน แต่ฉันไม่เห็นปัญหาที่จะซื่อสัตย์ แน่นอนว่า Python ไม่ใช่ภาษา FP แต่ Map / Reduce / Filter นั้นสามารถอ่านได้อย่างสมบูรณ์แบบและเป็นมาตรฐานสูงสุดของกรณีการใช้งานมาตรฐานในการเขียนโปรแกรมใช้งาน

ดังนั้นคุณไป รู้ว่าการเขียนโปรแกรมการทำงานของเจ้า

รายการเงื่อนไขการกรอง

มันจะไม่ง่ายไปกว่านี้:

next(filter(lambda x: x.val == value,  my_unfiltered_list)) # Optionally: next(..., None) or some other default value to prevent Exceptions

ฉันชอบสไตล์นี้มาก แต่มีสองประเด็นที่อาจเกิดขึ้น 1 : ใช้งานได้ใน Python 3 เท่านั้น ในหลาม 2 กลับรายการที่ไม่ได้เข้ากันได้กับfilter 2 : มันต้องการให้มีการแข่งขันที่ชัดเจนมิฉะนั้นคุณจะได้รับการยกเว้น nextStopIteration
Freethebees

1
1: ฉันไม่ทราบ Python 2 เมื่อฉันเริ่มใช้ Python Python 3 ก็พร้อมใช้งานแล้ว น่าเสียดายที่ฉันไม่รู้เรื่องเกี่ยวกับคุณสมบัติของ Python 2 2. @freethebees คุณสามารถใช้ถัดไป (... , ไม่มี) หรือค่าเริ่มต้นอื่น ๆ ได้หากคุณไม่ใช่แฟนของข้อยกเว้น ฉันยังเพิ่มมันเป็นความคิดเห็นในรหัสของฉัน
Nima Mousavi

@freethebees Point 2 อาจดีจริง ๆ เมื่อฉันต้องการวัตถุบางอย่างในรายการความล้มเหลวที่รวดเร็วเป็นสิ่งที่ดี
kap

7

ตัวอย่างง่ายๆ : เรามีอาร์เรย์ดังต่อไปนี้

li = [{"id":1,"name":"ronaldo"},{"id":2,"name":"messi"}]

ตอนนี้เราต้องการค้นหาวัตถุในอาร์เรย์ที่มี id เท่ากับ 1

  1. ใช้วิธีการที่nextมีรายการความเข้าใจ
next(x for x in li if x["id"] == 1 )
  1. ใช้รายการความเข้าใจและกลับรายการแรก
[x for x in li if x["id"] == 1 ][0]
  1. ฟังก์ชั่นที่กำหนดเอง
def find(arr , id):
    for x in arr:
        if x["id"] == id:
            return x
find(li , 1)

เอาท์พุทวิธีการทั้งหมดข้างต้นคือ {'id': 1, 'name': 'ronaldo'}


1

ฉันเพิ่งพบปัญหาที่คล้ายกันและวางแผนการเพิ่มประสิทธิภาพขนาดเล็กสำหรับกรณีที่ไม่มีวัตถุในรายการตรงตามความต้องการ (สำหรับกรณีการใช้งานของฉันสิ่งนี้ส่งผลในการปรับปรุงประสิทธิภาพที่สำคัญ):

พร้อมกับรายการ test_list ฉันเก็บชุดเพิ่มเติม test_value_set ซึ่งประกอบด้วยค่าของรายการที่ฉันต้องการกรอง ดังนั้นที่นี่โซลูชันอื่นของ agf จึงรวดเร็วมาก


1

คุณสามารถทำอะไรเช่นนี้

dict = [{
   "id": 1,
   "name": "Doom Hammer"
 },
 {
    "id": 2,
    "name": "Rings ov Saturn"
 }
]

for x in dict:
  if x["id"] == 2:
    print(x["name"])

นั่นคือสิ่งที่ฉันใช้เพื่อค้นหาวัตถุในอาร์เรย์ของวัตถุที่ยาว


แตกต่างกันอย่างไรแล้วผู้ตั้งคำถามคนใดได้ลองไปแล้ว
Anum Sheraz

ฉันต้องการแสดงให้เห็นว่าเขาสามารถทำให้วัตถุและชุดของวัตถุเป็นวิธีที่ง่ายที่สุดได้อย่างไร
ยกเว้น

0

คุณสามารถใช้การเปรียบเทียบที่หลากหลายผ่าน__eq__วิธีการสำหรับTestชั้นเรียนของคุณและใช้inตัวดำเนินการ ไม่แน่ใจว่านี่เป็นวิธีแบบสแตนด์อะโลนที่ดีที่สุด แต่ในกรณีที่คุณต้องการเปรียบเทียบTestอินสแตนซ์จากที่valueอื่นอาจเป็นประโยชน์ได้

class Test:
    def __init__(self, value):
        self.value = value

    def __eq__(self, other):
        """To implement 'in' operator"""
        # Comparing with int (assuming "value" is int)
        if isinstance(other, int):
            return self.value == other
        # Comparing with another Test object
        elif isinstance(other, Test):
            return self.value == other.value

import random

value = 5

test_list = [Test(random.randint(0,100)) for x in range(1000)]

if value in test_list:
    print "i found it"

0

สำหรับโค้ดด้านล่าง xGen เป็นนิพจน์เครื่องกำเนิดไฟฟ้าแบบอิสระ yFilt เป็นวัตถุตัวกรอง โปรดทราบว่าสำหรับ xGen พารามิเตอร์ None เพิ่มเติมจะถูกส่งกลับแทนที่จะโยน StopIteration เมื่อรายการหมด

arr =((10,0), (11,1), (12,2), (13,2), (14,3))

value = 2
xGen = (x for x in arr if x[1] == value)
yFilt = filter(lambda x: x[1] == value, arr)
print(type(xGen))
print(type(yFilt))

for i in range(1,4):
    print('xGen: pass=',i,' result=',next(xGen,None))
    print('yFilt: pass=',i,' result=',next(yFilt))

เอาท์พุท:

<class 'generator'>
<class 'filter'>
xGen: pass= 1  result= (12, 2)
yFilt: pass= 1  result= (12, 2)
xGen: pass= 2  result= (13, 2)
yFilt: pass= 2  result= (13, 2)
xGen: pass= 3  result= None
Traceback (most recent call last):
  File "test.py", line 12, in <module>
    print('yFilt: pass=',i,' result=',next(yFilt))
StopIteration
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.