从多个列表中创建唯一对象列表

4
我定义了一个包含多个字段的自定义对象。
例如,我有一个学生对象,它由姓名、ID和年龄组成。为了比较两个学生并确定他们是否是同一个学生,我实现了一个`__eq__`方法,该方法将返回两个学生的年龄、姓名和ID是否匹配。
``` def __eq__(self, other): return self.name == other.name and self.ID == other.ID and self.age == other.age ```
请注意,学生只是一个例子,因此不考虑学生ID通常是唯一的事实。
假设我有以下具有任意数量的学生对象的注册列表
``` [S1, S2, S3] [S2, S3] [S3, S5, S4] [S1, S4, S2, S1] ```
我想创建一些数据结构,其中包含以下元素
``` S1, S2, S3, S4, S5 ```
最简单的方法是初始化一些可以容纳大量内容的数据结构,获取一个项目,检查它是否存在于结构中,并在不存在时添加它。
``` new_list = some_new_list for each list of students: for each student in the list: check if the student is in new_list #decide what to do ```
如果我决定将其实现为简单的列表,则随着列表继续增长,我可能会进行大量比较,特别是如果我有大量学生和注册列表。
那么,如何高效地实现这一点?既比较两个对象,又使用该比较方法生成唯一的对象集。
编辑:我尝试了一个简单的集合实现。
``` a = Student("sample", 1234, 18) b = Student("sample", 1234, 18) students = set() students.add(a) b in students False b == a True ```
我做错了什么吗?

2
为什么不使用内置的set类型?成员测试可能比你在纯Python中所期望的要更有效率。 - Omri Barel
@omrib,所以遍历每个学生列表,然后调用newSet.add(student)就像这样表现得很好? - MxLDevs
@agf,哦,我原以为只需检查项目是否存在于集合中就足够了。即:“测试成员资格”。 - MxLDevs
1
你不需要检查它们是否在集合中。只需添加所有条目,集合最终将保存唯一的条目。这比逐个检查条目是否唯一要快。请参见我的__hash__编辑。 - agf
@agf,那可能就是了。不过,当我实现哈希方法并尝试使用相同的姓名ID和年龄(即:相同的学生)添加两个不同的学生对象时,它们都被添加到了集合中。 - MxLDevs
@agf,哦,现在它可以工作了。它们都散列到相同的值,只有其中一个在集合中。我可能打错了一个字母。 - MxLDevs
3个回答

8
from itertools import chain
myset = set(chain(iterable1, iterable2, iterable3, iterable4))

您可以得到独特的项目,并且只对每个可迭代对象进行一次迭代。 chain 从一系列可迭代对象中生成一个长迭代器。如果您需要排序,sorted(myset) 将为您提供已排序的列表。
您的 Student 类需要实现与其 __eq__ 兼容的 __hash__
def __hash__(self):
    return (self.name, self.ID, self.age).__hash__()

如果您有一定量的学生列表(全部包含在名为 student_lists 的列表或元组中),则可以使用 set(chain(*student_lists)) - Omri Barel
@omrib 使用 set(chain.from_iterable(student_lists)),这样就不需要解包 student_lists。与 mapzip 不同,编写 itertools.chain 的人已经考虑到了这一点。 - agf

0

set不能保证有序。如果你需要一个有序的列表:

import itertools
from typing import List

def unique_items(*lists: List) -> List:
    """Return an order-preserving list of unique items from the given lists.

    The implemented approach requires that the input items are hashable.

    Example: unique_items([1,9,4], [2,4,6,8,8], [3,1]) -> [1, 9, 4, 2, 6, 8, 3]

    Ref: https://dev59.com/m1rUa4cB1Zd3GeqPhS1R#68626841/
    """
    return list(dict.fromkeys(itertools.chain(*lists)))

-3

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接