Mencari daftar objek dengan Python

94

Mari kita asumsikan saya membuat kelas sederhana untuk bekerja mirip dengan struct C-style, untuk hanya menyimpan elemen data. Saya mencoba mencari cara untuk mencari daftar objek untuk objek dengan atribut yang sama dengan nilai tertentu. Di bawah ini adalah contoh sederhana untuk mengilustrasikan apa yang saya coba lakukan.

Contohnya:

class Data:
    pass

myList = []

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

Bagaimana cara mencari daftar myList untuk menentukan apakah itu berisi elemen dengan n == 5?

Saya telah Googling dan mencari dokumen Python, dan saya pikir saya mungkin bisa melakukan ini dengan pemahaman daftar, tapi saya tidak yakin. Saya mungkin menambahkan bahwa saya harus menggunakan Python 2.4.3, jadi fitur gee-whiz 2.6 atau 3.x baru tidak tersedia untuk saya.

m0j0
sumber
Mungkin kekhasan yang tidak disengaja dari contoh Anda: myList = [Data (). N == 0, Data (). N = 1, ...] di mana data.n akan ditetapkan oleh range () dan data.n akan menjadi indeks ke myList. Karenanya memungkinkan Anda untuk menarik instance Data () hanya dengan mereferensikan myList dengan nilai indeks. Tentu saja nanti Anda dapat memodifikasi myList [0] .n = 5.2 atau semacamnya. Dan contohnya mungkin terlalu disederhanakan.
DevPlayer

Jawaban:

139

Anda bisa mendapatkan daftar semua elemen yang cocok dengan pemahaman daftar:

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

Jika Anda hanya ingin menentukan apakah daftar berisi elemen apa pun yang cocok dan melakukannya (secara relatif) secara efisien, Anda dapat melakukannya

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
Adam Rosenfield
sumber
25
atau, sembarang (custom_filter (x) untuk x di myList jika xn == 30) yang hanya merupakan fungsi "berisi" Anda sebagai bawaan.
nosklo
Kesalahan sintaks pada nosklo - membutuhkan set tambahan () di sekitar generator.
gahooa
Tidak begitu. Cobalah dan lihat.
Robert Rossney
1
akan lebih baik untuk menggabungkan jawaban ini bersama dengan gahooa ( stackoverflow.com/a/598602/2349267 ).
Roman Hwang
77

Sederhana, Elegan, dan Kuat:

Ekspresi generator dalam hubungannya dengan builtin… (python 2.5+)

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

Menggunakan Python any()bawaan, yang didefinisikan sebagai berikut:

any (iterable) -> Kembalikan True jika ada elemen dari iterable yang benar. Setara dengan:

def any(iterable):
    for element in iterable:
        if element:
            return True
    return False
gahooa
sumber
Bagus. FYI Anda dapat melakukan apa saja (x untuk x di mylist jika xn == 10) untuk menyimpan beberapa parens (juga == tidak =).
Jacob Gabrielson
Saya lebih suka menggunakan any(x for x in mylist if x['n'] == 10)tapi ide yang bagus
Alex Montoya
48

Hanya untuk kelengkapan, jangan lupakan Hal Paling Sederhana yang Mungkin Bisa Berhasil:

for i in list:
  if i.n == 5:
     # do something with it
     print "YAY! Found one!"
Charlie Martin
sumber
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
Markus Jarderot
sumber
1
Ini adalah jawaban yang bagus karena metode "pertama", yang mungkin merupakan kasus penggunaan paling umum.
galarant
Terima kasih banyak! indeks pertandingan adalah apa yang saya cari. Apakah ada jalan pintas untuk menggunakan ini untuk langsung mengindeks daftar untuk mengakses bidang lain? Sekarang saya mendapatkan daftar entri daftar (hanya ada satu entri, jadi ini adalah daftar dengan satu item). Untuk mendapatkan indeks, saya perlu melakukan hasil [0] sebelum saya dapat menggunakannya untuk mengindeks daftar. Dari contoh pertanyaan, saya ingin mengakses n_squared dari n tertentu: myList [index of myList.n == 5] .n_squared
Frieke
32
filter(lambda x: x.n == 5, myList)
vartec
sumber
25
bagi seseorang yang ingin belajar Python, memahami lambda adalah hal mendasar.
vartec
2
Ya dan tidak - dengan pemahaman daftar dan penyortiran pembuat fungsi utama seperti operator.attrgetter, saya hampir tidak pernah menggunakan lambdas.
Ben Hoyt
9

Anda dapat menggunakan inuntuk mencari item dalam koleksi, dan pemahaman daftar untuk mengekstrak bidang yang Anda minati. Ini (berfungsi untuk daftar, set, tupel, dan apa pun yang mendefinisikan __contains__atau __getitem__).

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

Lihat juga:

Tom Dunham
sumber
4

Anda harus menambahkan a __eq__dan __hash__metode ke Datakelas Anda , itu bisa memeriksa apakah __dict__atributnya sama (properti yang sama) dan kemudian apakah nilainya sama juga.

Jika Anda melakukan itu, Anda dapat menggunakan

test = Data()
test.n = 5

found = test in myList

Kata inkunci memeriksa apakah testmasuk myList.

Jika Anda hanya ingin nproperti di DataAnda dapat menggunakan:

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
Johannes Weiss
sumber
3

Pertimbangkan untuk menggunakan kamus:

myDict = {}

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

print(5 in myDict)
dan-gph
sumber
Atau: d = dict ((i, i * i) for i in range (20))
hughdbrown
Ini memecahkan masalah sepele yang saya gunakan untuk mengilustrasikan pertanyaan saya, tetapi tidak benar-benar menyelesaikan pertanyaan akar saya. Jawaban yang saya cari (5+ tahun yang lalu) adalah pemahaman daftar. :)
m0j0
1

Cara lain yang bisa Anda lakukan adalah menggunakan fungsi next ().

matched_obj = next(x for x in list if x.n == 10)
Oliver Breeden
sumber