
本文探讨了在python `sortedcontainers`库的`sortedlist`中,如何高效地搜索自定义类对象。针对按字符串属性搜索的需求,我们分析了传统方法的局限性,并提出通过在自定义类中实现富比较方法(如`__lt__`和`__eq__`),以支持直接使用字符串进行二分查找,从而实现更简洁、更符合面向对象原则的解决方案。
sortedcontainers库提供的SortedList是一个功能强大的有序列表实现,它在保持元素有序的同时,提供了高效的插入、删除和查找操作。当SortedList中存储的是自定义类的对象时,我们通常会为其指定一个key函数来定义排序规则。例如,一个Supplier类的列表可能按其Name属性进行排序:
from typing import List
from sortedcontainers import SortedList
class Supplier:
def __init__(self, name: str, id: int, sap_id: int):
self.Name = name
self.Id = id
self.SapId = sap_id
def __repr__(self):
return f"Supplier(Name='{self.Name}', Id={self.Id})"
class Data:
def __init__(self):
# 初始 SortedList 按供应商名称(小写)排序
self.suppliers = SortedList(key=lambda x: x.Name.lower())
然而,当需要根据一个简单的字符串(例如供应商名称)在SortedList中查找对应的Supplier对象时,bisect_left等二分查找方法会遇到挑战。bisect_left期望比较的两个对象类型一致,或者至少能够相互比较。如果直接传入一个字符串,而SortedList中存储的是Supplier对象,默认情况下它们无法直接比较,导致查找失败。
为了解决上述问题,常见的尝试包括:
直接使用bisect_left(name): 这种方法会因为str和Supplier类型不兼容而报错,因为SortedList的key函数只影响排序,不改变bisect_left在内部进行比较时的数据类型。
创建临时对象进行查找: 一种可行的变通方法是创建一个临时的Supplier对象,只填充用于比较的Name属性,然后用这个临时对象进行查找:
# Data 类的一部分
def find_supplier_with_temp_object(self, name: str) -> Supplier | None:
temporary_supplier = Supplier(name, 0, 0) # 创建临时对象
index = self.suppliers.bisect_left(temporary_supplier)
if index != len(self.suppliers) and self.suppliers[index].Name.lower() == name.lower():
return self.suppliers[index]
return None这种方法虽然能够实现功能,但它引入了不必要的临时对象创建,增加了代码的复杂性,并且在每次查找时都重复创建对象,显得不够优雅和高效。
立即学习“Python免费学习笔记(深入)”;
Python的面向对象特性允许我们通过实现“富比较方法”(rich comparison methods),如__lt__(小于)、__le__(小于等于)、__eq__(等于)等,来定义对象之间的比较行为。通过在Supplier类中实现这些方法,我们可以让Supplier对象能够直接与字符串进行比较,从而简化SortedList的查找逻辑。
为了实现按名称进行大小写不敏感的排序和查找,我们需要修改Supplier类,使其能够与str类型以及其他Supplier类型进行比较。
class Supplier:
def __init__(self, name: str, id: int = 0, sap_id: int = 0):
self.Name = name
self.Id = id
self.SapId = sap_id
def __repr__(self):
return f"Supplier(Name='{self.Name}')"
# 定义小于比较行为,支持与字符串和Supplier对象比较
def __lt__(self, other):
if isinstance(other, str):
# 将自身名称和小写化的other字符串进行比较
return self.Name.lower() < other.lower()
elif isinstance(other, Supplier):
# 将自身名称和小写化的other Supplier名称进行比较
return self.Name.lower() < other.Name.lower()
return NotImplemented # 不支持与其他类型比较
# 定义等于比较行为,支持与字符串和Supplier对象比较
def __eq__(self, other):
if isinstance(other, str):
return self.Name.lower() == other.lower()
elif isinstance(other, Supplier):
return self.Name.lower() == other.Name.lower()
return NotImplemented # 不支持与其他类型比较
# 建议也实现 __gt__, __le__, __ge__, __ne__ 以提供完整的比较逻辑
def __gt__(self, other):
if isinstance(other, str):
return self.Name.lower() > other.lower()
elif isinstance(other, Supplier):
return self.Name.lower() > other.Name.lower()
return NotImplemented
def __le__(self, other):
if isinstance(other, str):
return self.Name.lower() <= other.lower()
elif isinstance(other, Supplier):
return self.Name.lower() <= other.Name.lower()
return NotImplemented
def __ge__(self, other):
if isinstance(other, str):
return self.Name.lower() >= other.lower()
elif isinstance(other, Supplier):
return self.Name.lower() >= other.lower()
return NotImplemented
def __ne__(self, other):
return not self.__eq__(other)
通过实现__lt__和__eq__方法,Supplier对象现在可以“理解”如何与字符串进行比较。这意味着:
现在,Data类中的SortedList初始化和find_supplier方法可以变得更加简洁:
class Data:
def __init__(self):
# SortedList 现在可以直接使用 Supplier 对象的 __lt__ 等方法进行排序
self.suppliers = SortedList()
def find_supplier(self, name: str) -> Supplier | None:
# bisect_left 直接使用字符串进行查找
index = self.suppliers.bisect_left(name)
# 检查找到的索引是否有效,并且对应的供应商名称是否匹配
if index != len(self.suppliers) and self.suppliers[index].Name.lower() == name.lower():
return self.suppliers[index]
return None
# 完整示例
if __name__ == "__main__":
d = Data()
# 添加供应商
d.suppliers.add(Supplier('Apple Inc.', 101, 1001))
d.suppliers.add(Supplier('Banana Corp.', 102, 1002))
d.suppliers.add(Supplier('Cherry Ltd.', 103, 1003))
d.suppliers.add(Supplier('apple holdings', 104, 1004)) # 名称大小写不同
print("SortedList 内容:", d.suppliers) # 此时会按名称小写排序
# 查找供应商
found_supplier_apple = d.find_supplier('apple inc.')
print(f"\n查找 'apple inc.': {found_supplier_apple}")
found_supplier_banana = d.find_supplier('Banana Corp.')
print(f"查找 'Banana Corp.': {found_supplier_banana}")
found_supplier_grape = d.find_supplier('Grape Co.')
print(f"查找 'Grape Co.': {found_supplier_grape}")
found_supplier_apple_holdings = d.find_supplier('apple holdings')
print(f"查找 'apple holdings': {found_supplier_apple_holdings}")输出示例:
SortedList 内容: [Supplier(Name='Apple Inc.'), Supplier(Name='apple holdings'), Supplier(Name='Banana Corp.'), Supplier(Name='Cherry Ltd.')] 查找 'apple inc.': Supplier(Name='Apple Inc.') 查找 'Banana Corp.': Supplier(Name='Banana Corp.') 查找 'Grape Co.': None 查找 'apple holdings': Supplier(Name='apple holdings')
从输出可以看出,SortedList正确地将'Apple Inc.'和'apple holdings'相邻排序,并且find_supplier方法能够通过大小写不敏感的字符串查找,准确地返回对应的Supplier对象。
通过在自定义类中巧妙地实现富比较方法,我们可以让SortedList中的对象与外部的简单类型(如字符串)进行直接、高效且符合逻辑的比较。这种方法不仅消除了创建临时对象的“丑陋”代码,还提升了代码的清晰度、可维护性和面向对象的设计原则。它提供了一种更自然、更Pythonic的方式来处理SortedList中自定义对象的复杂搜索需求。
以上就是优化Python SortedList中自定义对象的高效搜索的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号