Python'da nesnelerin bir listesini arama


94

Sadece veri öğelerini tutmak için C tarzı bir yapıya benzer şekilde çalışmak üzere basit bir sınıf oluşturduğumu varsayalım. Belirli bir değere eşit bir niteliğe sahip nesneler için bir nesne listesinde nasıl arama yapılacağını anlamaya çalışıyorum. Aşağıda, yapmaya çalıştığım şeyi göstermek için önemsiz bir örnek var.

Örneğin:

class Data:
    pass

myList = []

for i in range(20):
    data = Data()
    data.n = i
    data.n_squared = i * i
    myList.append(data)

N == 5 olan bir öğe içerip içermediğini belirlemek için myList listesinde arama yapmaya nasıl devam edebilirim?

Googling yapıyorum ve Python belgelerini araştırıyorum ve sanırım bunu bir liste anlayışı ile yapabilirim, ancak emin değilim. Bu arada Python 2.4.3'ü kullanmak zorunda olduğumu ekleyebilirim, bu yüzden yeni gee-whiz 2.6 veya 3.x özellikleri benim için mevcut değil.


Örneğinizin kasıtsız bir tuhaflığı olabilir: myList = [Data (). N == 0, Data (). N = 1, ...] burada data.n, range () ve data.n tarafından atanır myList'e dizin. Bu nedenle, herhangi bir Data () örneğini yalnızca bir dizin değerine göre myList'e başvurarak çekmenize izin verir. Elbette daha sonra myList [0] .n = 5.2'yi veya başka bir şeyi değiştirebilirsiniz. Ve örnek belki de fazla basitleştirildi.
DevPlayer

Yanıtlar:


139

Sen bir listesini alabilirsiniz tüm listesi anlayışı ile eşleşen elemanları:

[x for x in myList if x.n == 30]  # list of all elements with .n==30

Listenin eşleşen herhangi bir öğe içerip içermediğini belirlemek ve bunu (nispeten) verimli bir şekilde yapmak istiyorsanız, şunları yapabilirsiniz:

def contains(list, filter):
    for x in list:
        if filter(x):
            return True
    return False

if contains(myList, lambda x: x.n == 3)  # True if any element has .n==3
    # do stuff

25
veya herhangi bir (custom_filter (x), myList'te xn == 30 ise) yerleşik olarak "içerir" işlevinizdir.
nosklo

Nosklo'da sözdizimi hatası - oluşturucu etrafında fazladan bir () kümesi gerekir.
gahooa

Öyle değil. Deneyin ve görün.
Robert Rossney

1
bu yanıtı gahooa'nın ( stackoverflow.com/a/598602/2349267 ) ile birleştirmek iyi olur .
Roman Hwang

77

Basit, Zarif ve Güçlü:

Yerleşik bir… (python 2.5+) ile birlikte bir oluşturucu ifadesi

any(x for x in mylist if x.n == 10)

any()Aşağıdaki gibi tanımlanan Python yerleşikini kullanır :

herhangi (yinelenebilir) Yinelenebilir -> öğenin herhangi bir öğesi doğruysa True döndür. Eşittir:

def any(iterable):
    for element in iterable:
        if element:
            return True
    return False

Güzel. Bilginize, bazı parensleri (ayrıca == değil =) kaydetmek için herhangi bir (mylist'te x için x) yapabilirsiniz.
Jacob Gabrielson

any(x for x in mylist if x['n'] == 10)
Alex Montoya

48

Sadece eksiksizlik adına, İşe Yarayabilecek En Basit Şeyi unutmayalım:

for i in list:
  if i.n == 5:
     # do something with it
     print "YAY! Found one!"

39
[x for x in myList if x.n == 30]               # list of all matches
[x.n_squared for x in myList if x.n == 30]     # property of matches
any(x.n == 30 for x in myList)                 # if there is any matches
[i for i,x in enumerate(myList) if x.n == 30]  # indices of all matches

def first(iterable, default=None):
  for item in iterable:
    return item
  return default

first(x for x in myList if x.n == 30)          # the first match, if any

1
Bu, muhtemelen en yaygın kullanım durumu olan "ilk" yöntem nedeniyle iyi bir yanıttır.
galarant

çok teşekkürler! maç endeksleri aradığım şeydi. Başka bir alana erişmek için listeyi doğrudan indekslemek için bunu kullanmanın bir kısayolu var mı? Şimdi liste girişlerinin bir listesini alıyorum (sadece bir giriş var, yani tek öğeli bir liste). İndeksi almak için, listeyi indekslemede kullanmadan önce [0] sonucunu gerçekleştirmem gerekiyor. Soru örneğinden, n_squared'e belirli bir n: myList [index of myList.n == 5] .n_squared
Frieke

32
filter(lambda x: x.n == 5, myList)

25
Python öğrenmek isteyen biri için lambda'yı anlamak çok önemlidir.
vartec

2
Evet ve hayır - operatör.attrgetter gibi liste anlamaları ve anahtar işlevleri sıralamasıyla, neredeyse hiç lambdas kullanmıyorum .
Ben Hoyt

9

Sen kullanabilirsiniz inbir koleksiyon bir öğenin ve ilgilenen edilir alanını ayıklamak için bir liste anlama aramaya. Bu (çalışıyor listeleri, setleri, küpe ve bir şey olduğunu tanımlar için __contains__ya __getitem__).

if 5 in [data.n for data in myList]:
    print "Found it"

Ayrıca bakınız:


4

Sınıfınıza bir __eq__ve bir __hash__yöntem eklemelisiniz Data, bu,__dict__ özniteliklerin eşit (aynı özellikler) ve sonra değerlerinin de eşit olup olmadığını kontrol edebilir.

Bunu yaptıysan, kullanabilirsin

test = Data()
test.n = 5

found = test in myList

inEğer anahtar kelime kontroller testisemyList .

Yalnızca bir nmülk istiyorsanız , Dataşunları kullanabilirsiniz:

class Data(object):
    __slots__ = ['n']
    def __init__(self, n):
        self.n = n
    def __eq__(self, other):
        if not isinstance(other, Data):
            return False
        if self.n != other.n:
            return False
        return True
    def __hash__(self):
        return self.n

    myList = [ Data(1), Data(2), Data(3) ]
    Data(2) in myList  #==> True
    Data(5) in myList  #==> False

3

Bir sözlük kullanmayı düşünün:

myDict = {}

for i in range(20):
    myDict[i] = i * i

print(5 in myDict)

Veya: d = dict ((i, i * i) aralıktaki i için (20))
hughdbrown

Sorumu açıklamak için kullandığım önemsiz sorunu çözdü, ancak asıl sorumu gerçekten çözmedi. Aradığım cevap (5+ yıl önce) liste anlayışıydı. :)
m0j0

1

Bunu yapmanın başka bir yolu da next () işlevini kullanmaktır.

matched_obj = next(x for x in list if x.n == 10)
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.