【问题标题】:Python: How to find the intersection between two lists based on object's id?Python:如何根据对象的 id 找到两个列表之间的交集?
【发布时间】:2019-07-01 01:45:25
【问题描述】:

我知道如果我有两个列表,比如整数,我可以简单地使用list(set(list1) & set(list2)) 来获得交集。但是,在我的两个列表中,我有可变对象,即Nodes。 Node是一个可以用值初始化的类。

无需执行双 for 循环,有没有办法根据它们的 id 获取两个列表的交集?我正在寻找类似于list(set(list1) & set(list2)) 的东西。

更新:我指的是 Python 中内置的 id() 函数,它返回对象在内存中的存储地址。

所以,我在问 [Node1, Node2, Node3] 和 [Node100, Node2, Node3] 的交集是什么。显然我不能使用上面的设置交集方法。我需要通过访问内存来识别它们是否相同。如果我不能尝试根据它们的 value 属性来识别它们,因为它们可能 Node1 可能与 Node100 具有相同的值,但它们不是内存中的相同对象。

【问题讨论】:

  • “基于他们的 id”是什么意思? id 是Node 对象的属性吗?
  • “id”是指对可能同时出现在list1 和list2 中的相同对象的引用吗?
  • @Primusa。你为什么删除你的答案?
  • @MadPhysicist Philip 有一个更好的答案。我没有必要跟上我的;我赞成他的回答并继续前进。
  • 确定我不清楚:如果它们在内存中不是同一个对象,您是否希望在最终列表中保留两个具有相同值的节点?

标签: python


【解决方案1】:

没有必要让两个集合相交。在这种情况下,您只需检查 id() 是否存在于另一个集合中。

set2 = {id(n) for n in list2}
result = [n for n in list1 if id(n) in set2]

这段代码的复杂度是O(n1 + n2)。我将在以下等效但更易读的代码中对此进行解释:

set2 = {id(n) for n in list2}  # O(n2)
result = []
for n in list1:  # O(n1)
    if id(n) in set2:  # O(1)
        result.append(n)  # O(1)

总共是O(n1 + n2)。


如果您可以通过定义__hash__ 和__eq__ 方法来更改Node 类,还有一个替代解决方案。

class Node:
    ...

    def __hash__(self):
        return id(self)

    def __eq__(self, another):
        return id(self) == id(another)


list1 = [...]
list2 = [...]

result = set(list1) & set(list2)

【讨论】:

  • 我的意思是python的id内置函数,不是我的属性
  • 另外,这只是一个 for 循环
  • @MadPhysicist 好吧,您必须先将列表转换为设置。在内部,set() 初始化程序也将遍历整个列表(我认为它算作一个循环)。
  • @AlanH。这不仅仅是一个 for 循环。集合构造为 O(n2),循环为 O(n1)。这使它成为 O(n1+n2),而不是 O(n1*n2)。对于可比较的列表大小,您最终会得到 O(n) 而不是 O(n^2)。您可以用 id(n) 轻松替换 n.id
  • @MadPhysicist 你能解释一下为什么它是 O(list1 + list2) 吗?如果我有一个 for 循环,并且对于每次迭代,我都必须创建一组 k 元素,那不只是乘以运行时间吗?
【解决方案2】:

您建议的解决方案将起作用。

class Node:
    def __init__(self, value):
        self.value = value

    def __repr__(self):
        return "Node {}".format(self.value)

nodes1 = [Node(1), Node(2), Node(3)]
nodes2 = nodes1[:2] + [Node(4)]

common_nodes = set(nodes1) & set(nodes2)

print(common_nodes) # {Node 2, Node 1}

这样做的原因是因为尽管是可变的,但您未定义 __hash__ 或 __eq__ 的类的实例默认情况下将通过其 id 进行散列和比较,因为它从 @ 继承了这些方法987654325@.

您可以通过以下实验来验证这一点。

>>> obj = object()
>>> hash(obj)
155115580943
>>> id(obj)
2481849295088
>>> id(obj) // 16 == hash(obj)
True

【讨论】:

    猜你喜欢
    • 2021-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-06
    • 2023-03-27
    • 2011-06-28
    • 2013-08-07
    • 2018-04-30
    相关资源
    最近更新 更多