Python自定义集合交集

2024-06-02 06:27:40 发布

您现在位置:Python中文网/ 问答频道 /正文

因此,有一种简单的方法可以通过set.intersection()计算两个集合的交集。但是,我有以下问题:

class Person(Object):                    
    def __init__(self, name, age):                                                      
        self.name = name                                                                
        self.age = age                                                                  

l1 = [Person("Foo", 21), Person("Bar", 22)]                                             
l2 = [Person("Foo", 21), Person("Bar", 24)]                                             

union_list = list(set(l1).union(l2))                                           
# [Person("Foo", 21), Person("Bar", 22), Person("Bar", 24)]

Object是由我的ORM提供的一个基类,它实现了基本的__hash____eq__功能,基本上将类的每个成员都添加到散列中。换句话说,返回的__hash__将是类中每个元素的哈希值)

在这个阶段,我只想通过.name来运行一个集合交集操作,以找到,比如说Person('Bar', -1).intersection(union_list) #= [Person("Bar", -1), Person("Bar", 22), Person("Bar", 24)]。(此时,典型的.intersection()不会给我任何东西,我不能覆盖__hash____eq__上的Person类,因为这将覆盖原始的集合联合(Ithink

在Python2.x中,最好的方法是什么?

编辑:请注意,该解决方案不依赖于aset。不过,我需要找到联合,然后交叉点,所以感觉这是一套可行的(但我愿意接受的解决方案,使用任何魔术,你认为值得,只要它解决了我的问题!)


Tags: 方法nameselfl1ageobjectfoobar
3条回答

我讨厌回答我自己的问题,所以我暂时不把它当作“答案”。

结果是这样做的:

import types
p = Person("Bar", -1)
new_hash_method = lambda obj: hash(obj.name)
p.__hash__ = types.MethodType(new_hash_method, p)
for i in xrange(0, len(union_list)):
    union_list[i].__hash__ = types.MethodType(new_hash_method, union_list[i])
set(union_list).intersection(p)

它当然是脏的,并且依赖于types.MethodType,但是它比目前提出的最佳解决方案(glglglgl的解决方案)要少,因为我的实际union_list可能包含数千个项目的顺序,所以这将节省我每次运行此交集过程时重新创建对象的时间。

听起来像

>>> class Person:
...     def __init__(self, name, age):
...         self.name = name
...         self.age = age
...     def __eq__(self, other):
...         return self.name == other.name
...     def __hash__(self):
...         return hash(self.name)
...     def __str__(self):
...         return self.name
...
>>> l1 = [Person("Foo", 21), Person("Bar", 22)]
>>> l2 = [Person("Foo", 21), Person("Bar", 24)]
>>> union_list = list(set(l1).union(l2))
>>> [str(l) for l in union_list]
['Foo', 'Bar']

是你想要的,因为name是你唯一的密钥?

如果您希望age与比较无关,那么应该重写Person中的__hash__()__eq__(),尽管您的Object中有它。

如果您只在这种(和类似的)上下文中需要这种行为,那么可以创建一个包装器对象,该对象包含Person,并且行为不同,例如

class PersonWrapper(Object):
    def __init__(self, person):
        self.person = person
    def __eq__(self, other):
        if hasattr(other, 'person'):
            return self.person.name == other.person.name
        else:
            return self.person.name == other.name
    def __hash__(self):
        return hash(self.person.name)

然后做

union_list = list(set(PersonWrapper(i) for i in l1).union(PersonWrapper(i) for i in l2))
# [Person("Foo", 21), Person("Bar", 22), Person("Bar", 24)]

(未测试)

相关问题 更多 >