[英]Remove duplicates in list of object with Python
我有一个对象列表,我有一个充满记录的数据库表。 我的对象列表有一个标题属性,我想从列表中删除所有具有重复标题的对象(保留原始标题)。
然后我想检查我的对象列表是否有数据库中任何记录的重复项,如果有,在将它们添加到数据库之前从列表中删除这些项目。
我已经看到了从这样的列表中删除重复项的解决方案: myList = list(set(myList))
,但我不确定如何使用对象列表来做到这一点?
我也需要维护我的对象列表的顺序。 我也在想也许我可以使用difflib
来检查标题中的差异。
set(list_of_objects)
仅在您知道重复项是什么时才会删除重复项,也就是说,您需要定义对象的唯一性。
为此,您需要使对象可散列。 您需要同时定义__hash__
和__eq__
方法,方法如下:
http://docs.python.org/glossary.html#term-hashable
不过,您可能只需要定义__eq__
方法。
编辑:如何实现__eq__
方法:
正如我提到的,您需要知道对象的唯一性定义。 假设我们有一本书的属性 author_name 和 title 它们的组合是唯一的,(所以,我们可以有很多史蒂芬金的书,很多书叫闪灵,但只有一本斯蒂芬金的书叫闪灵),那么实现如下:
def __eq__(self, other):
return self.author_name==other.author_name\
and self.title==other.title
同样,这就是我有时实现__hash__
方法的方式:
def __hash__(self):
return hash(('title', self.title,
'author_name', self.author_name))
您可以检查,如果您创建了一个包含相同作者和标题的 2 本书的列表,则书对象将相同(使用相等(使用is
运算符)和==
运算符)。 此外,当使用set()
时,它将删除一本书。
编辑:这是我的一个旧答案,但我现在才注意到它有一个错误,在最后一段中用删除线纠正:与is
相比,具有相同hash()
对象不会给出True
。 但是,如果您打算将它们用作集合的元素或字典中的键,则使用对象的可散列性。
由于它们不可散列,因此您不能直接使用集合。 标题应该是。
这是第一部分。
seen_titles = set()
new_list = []
for obj in myList:
if obj.title not in seen_titles:
new_list.append(obj)
seen_titles.add(obj.title)
您将需要描述您在第二部分使用的数据库/ORM 等。
这似乎很小:
new_dict = dict()
for obj in myList:
if obj.title not in new_dict:
new_dict[obj.title] = obj
__hash__
需要__hash__
和__eq__
。
需要__hash__
将对象添加到集合中,因为python 的集合是作为 hashtables 实现的。 默认情况下,像数字、字符串和元组这样的不可变对象是可散列的。
然而,由于鸽巢原理,散列冲突(两个不同的对象散列到相同的值)是不可避免的。 因此,不能仅使用哈希来区分两个对象,用户必须指定自己的__eq__
函数。 因此,用户提供的实际散列函数并不重要,尽管为了性能最好尽量避免散列冲突(请参阅什么是实现 __hash__() 的正确和好方法? )。
我最近最终使用了下面的代码。 它类似于其他答案,因为它遍历列表并记录它所看到的内容,然后删除它已经看到的任何项目,但它不会创建重复的列表,而只是从原始列表中删除该项目。
seen = {}
for obj in objList:
if obj["key-property"] in seen.keys():
objList.remove(obj)
else:
seen[obj["key-property"]] = 1
如果您不能(或不会)为对象定义__eq__
,则可以使用 dict-comprehension 来实现相同的目的:
unique = list({item.attribute:item for item in mylist}.values())
请注意,这将包含给定键的最后一个实例,例如对于mylist = [Item(attribute=1, tag='first'), Item(attribute=1, tag='second'), Item(attribute=2, tag='third')]
你得到[Item(attribute=1, tag='second'), Item(attribute=2, tag='third')]
。 您可以使用mylist[::-1]
(如果存在完整列表)来解决此问题。
对于不可散列的类型,您可以使用字典理解来根据所有对象中的字段删除重复对象。 这对于默认不支持可散列类型的Pydantic 特别有用:
{ row.title : row for row in rows }.values()
请注意,这将仅基于row.title
考虑重复项,并将最后匹配的 object 用于row.title
。 这意味着如果您的行可能具有相同的标题但在其他属性中具有不同的值,那么这将不起作用。
例如[{"title": "test", "myval": 1}, {"title": "test", "myval": 2}] ==> [{"title": "test", "myval": 2}]
如果你想匹配row
中的多个字段,你可以进一步扩展它:
{ f"{row.title}\0{row.value}" : row for row in rows }.values()
null 字符\0
用作字段之间的分隔符。 这假定 null 字符未在row.title
或row.value
中使用。
如果您想保留原始订单,请使用它:
seen = {}
new_list = [seen.setdefault(x, x) for x in my_list if x not in seen]
如果您不关心订购,请使用它:
new_list = list(set(my_list))
它很容易朋友: -
a = [5,6,7,32,32,32,32,32,32,32,32]
a = 列表(集合(a))
打印(一)
[5,6,7,32]
就是这样 ! :)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.