Listede bir değere eşit bir özniteliğe sahip herhangi bir nesneyi bulma (herhangi bir koşulu karşılayan)


221

Nesnelerin listesi var. Bu listede öznitelik (veya yöntem sonucu - ne olursa olsun) eşit olan bir (ilk veya her ne olursa olsun) bir nesne bulmak istiyorum value.

Onu bulmanın en iyi yolu nedir?

İşte test örneği:

  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

Ben jeneratörler kullanarak düşünüyorum ve reduce()herhangi bir fark olmaz çünkü hala liste üzerinden yineleme olacaktır.

ps .: Denklemi valuesadece bir örnektir. Tabii ki her koşulu karşılayan unsur almak istiyoruz.


2
İşte bu sorunun güzel bir tartışması: tomayko.com/writings/cleanest-python-find-in-list-function
Andrew Hare

Orijinal gönderi gülünç bir şekilde güncel değil, ancak 2. yanıt tek satırlı sürümümle tam olarak eşleşiyor. Yine de temel döngü sürümünden daha iyi olduğuna ikna olmadım.
agf

Yanıtlar:


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

Bu, listeden koşulla eşleşen ilk öğeyi alır ve döndürür None hiçbir öğe eşleşmezse . Bu benim tercih ettiğim tek ifadeli form.

Ancak,

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

Saf loop-break versiyonu, mükemmel Pythonic - özlü, net ve verimli. Tek astarın davranışıyla eşleşmesi için:

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

Bu atayacaktır Noneiçin xbunu yapmazsan breakdöngünün dışında.


72
Güven verici +1 için "Naif loop-break versiyonu, mükemmel Pythonic".
LaundroMat

harika bir çözüm, ama nasıl x.value aslında x.fieldMemberName bu adın değeri depolandığı anlamına gelebilir böylece satırınızı nasıl değiştirebilirim? field = "name" next ((x.field == değer ise test_list'deki x için x), None), bu durumda aslında x.field'a değil, x.name'ye karşı kontrol ediyorum
Stewart Dale

3
@StewartDale Ne istediğini tam olarak belli değil, ama demek istediğini anladım ... if getattr(x, x.fieldMemberName) == value. Bu, özniteliği xdepolanan adla getirecek fieldMemberNameve ile karşılaştıracaktır value.
agf

1
@ThatTechGuy - elseMadde fordöngüde değil, döngüde olmalıdır if. (Düzenleme Reddedildi).
agf

1
@agf Vay Gerçekten var olan hiçbir fikrim yoktu .. book.pythontips.com/en/latest/for_-_else.html harika!
ThatTechGuy

25

Çünkü sadece tamamlanmak için bahsedilmedi. Filtrelenecek öğeleri filtrelemek için iyi ol 'filtresi.

Fonksiyonel programlama 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.

Genelde python listesindeki anlamaların tercih edildiğini biliyorum ya da en azından okuduğum şey bu, ama konunun dürüst olduğunu görmüyorum. Elbette Python bir FP dili değildir, ancak Harita / Küçült / Filtre mükemmel bir şekilde okunabilir ve fonksiyonel programlamada en standart standart kullanım durumlarıdır.

Al işte ozaman, buyur. Fonksiyonel programlamayı bilir.

filtre koşulu listesi

Bundan daha kolay olmayacak:

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

Bunun tarzını çok seviyorum ama iki potansiyel sorun var. 1 : Yalnızca Python 3'te çalışır; Python 2'de, filteruyumlu olmayan bir liste döndürür next. 2 : kesin bir eşleşme olmasını gerektirir, aksi takdirde bir StopIterationistisna alırsınız .
freethebees

1
1: Python 2'nin farkında değilim. Python'u kullanmaya başladığımda Python 3 zaten mevcuttu. Ne yazık ki Python 2 özellikleri hakkında clueless. 2. @freethebees agf tarafından işaret edildiği gibi. İstisna hayranı değilseniz sonraki (..., Hiçbiri) veya başka bir varsayılan değer kullanabilirsiniz. Ayrıca koduma yorum olarak ekledim.
Nima Mousavi

@freethebees Point 2 aslında iyi olabilir. Bir listede belirli bir nesneye ihtiyaç duyduğumda hızlı başarısız olmak iyi bir şeydir.
kap

7

Basit bir örnek : Aşağıdaki diziye sahibiz

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

Şimdi, dizideki kimliği 1'e eşit olan nesneyi bulmak istiyoruz

  1. nextListe anlama yöntemini kullanma
next(x for x in li if x["id"] == 1 )
  1. Liste kavrayışı kullanın ve ilk öğeyi döndürün
[x for x in li if x["id"] == 1 ][0]
  1. Özel İşlev
def find(arr , id):
    for x in arr:
        if x["id"] == id:
            return x
find(li , 1)

Yukarıdaki tüm yöntemleri çıktı {'id': 1, 'name': 'ronaldo'}


1

Sadece benzer bir sorunla karşılaştım ve listedeki hiçbir nesnenin gereksinimi karşılamadığı durum için küçük bir optimizasyon tasarladım.

Test_list listesi ile birlikte, filtrelemem gereken listenin değerlerinden oluşan ek bir set test_value_set tutarım. Yani burada agf çözümünün diğer kısmı çok hızlı oluyor.


1

Böyle bir şey yapabilirsin

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

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

Nesneleri uzun bir dizi nesneleri bulmak için kullandığım bu.


Bu, zaten sorgulayıcı olanın denemesinden ne kadar farklı?
Anum Sheraz

En basit şekilde nesnenin ve nesnelerin dizisinin nasıl alınabileceğini göstermek istedim.
Illud

0

Sınıfınız ve kullanım operatörünüz __eq__için yöntemle zengin karşılaştırma da yapabilirsiniz . Bunun en iyi bağımsız yol olup olmadığından emin değilim, ancak örnekleri başka bir yere göre karşılaştırmanız gerekirse , bu yararlı olabilir.TestinTestvalue

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

Aşağıdaki kod için, xGen bir anonomik üreteç ifadesidir, yFilt bir filtre nesnesidir. XGen için, liste bittiğinde StopIteration öğesini atmak yerine ek None parametresinin döndürüldüğünü unutmayın.

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

Çıktı:

<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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.