在Python中搜索对象列表

2 投票
5 回答
3821 浏览
提问于 2025-04-16 06:57

我有一个包含多个物品(Item)对象的列表,每个物品都有一个日期属性。同时,我从数据库中获取了一个单独的日期。

我想在这个列表中查找所有日期晚于我从数据库获取的日期的物品。

我的物品列表里有上千个对象,所以我希望能尽可能高效地完成这个任务。

我觉得逐个遍历列表中的每个物品,检查它们的日期是否晚于我从数据库得到的日期,这样做可能不是最有效的方法。

class Item(object):    
    def __init__(self, title, link, description, date):
        self.title = title
        self.link = link
        self.description = description
        self.date = date

item_list = [] 
...
#assume I populate the list with a 1,000 Item objects

filtered_list = []
for it in item_list:
    if date_from_db > it.date:
        filtered_list.append(it)

5 个回答

1

针对有人说这个列表生成式让人困惑,我想分享一种更清晰的格式。我最近经常用这个。

filtered_list = [item                         # What we're collecting
                 for item in item_list        # What we're collecting it over 
                 if date_from_db < item.date] # the conditions

虽然这样会把原本可以用一行代码写成的内容变成三行,就像普通的for循环那样,但在很多情况下,甚至在这里,它都能提高可读性,同时还能让你享受到更高的效率。

2

要避免逐个检查列表中的每个项目,最简单的方法是先按日期排序,然后从后往前查找,直到找到一个比目标日期大的最后一个项目,然后把它们一个个加到你的 filtered_list 里。

或者,你也可以把列表按降序排序,然后从前往后查找,直到找到第一个比目标日期大的项目。这样你就可以轻松地修改你的循环,像这样:

filtered_list = [] 
for it in item_list: 
    if date_from_db > it.date: 
        filtered_list.append(it) 
    else:
        break

另外,如果你预计过滤后的列表会有很多项目,使用 二分查找 来找到第一个符合条件的项目可能会更快,然后用列表切片把它复制到 filtered_list 中:

first = binary_search(item_list, lambda it: cmp(date_from_db, it.date))
if first == -1:
    return []
return item_list[first:]

这里有一个我从上面链接中改编的二分查找函数。我相信它应该能正常工作:

def binary_search(a, comp, lo=0, hi=None): 
    if hi is None: 
        hi = len(a) 
    while lo < hi: 
        mid = (lo+hi)//2 
        cmpval = comp(a[mid])
        if cmpval < 0:
            lo = mid+1 
        elif cmpval > 0:
            hi = mid 
        else: 
            return mid 
    return -1 
5

列表推导是一种相对高效的方法,可以在不使用数据库的情况下完成这个任务:

[it for it in item_list if date_from_db > it.date]

另外,你也可以使用内置的 filter 函数:

filter(lambda it: it if date_from_db > it.date, item_list)

撰写回答