Python collections.Counter中的对象相等性

4 投票
2 回答
1766 浏览
提问于 2025-04-17 14:34

我有一个 collections.Counter 类的实例,同时我还有一些像这样的对象:

p1 = Person(name='John')
p2 = Person(name='John')
p3 = Person(name='Jane')

我想在 Counter 的实例中记录这些人对象的数量,注意同名的人对象应该增加同一个人的计数。所以,如果我有一个包含所有人对象的列表:

people = [p1, p2, p3]

然后我用这个列表来填充我的计数器:

c = Counter(people)

我希望得到以下结果:

c[p1] #prints 2
c[p2] #prints 2
c[p3] #prints 1

我第一次尝试是为人对象实现一个新的 __eq__ 方法。

def __eq__(self, other):
  return self.name == other.name

我觉得这样可以,因为计数器对象似乎是根据键对象的相等性来增加计数的,就像在:

c = Counter(['A', 'A', 'B'])
c['A'] #prints 2
c['B'] #prints 1

另一种尝试是从 Counter 继承,并重写 Counter 用来判断对象相等性的底层方法。我不太确定,但我觉得 Counter 是用 __contains__ 方法来实现的。

我的问题是,有没有办法在不使用继承的情况下实现这种行为,如果没有,最好的方法是什么呢?

2 个回答

2

如果你的对象很简单,就像你举的例子那样,可以使用 collections.namedtuple

from collections import Counter, namedtuple
Person = namedtuple('Person','name')

n1 = Person(name='John')
n2 = Person(name='John')
n3 = Person(name='Jane')
Counter((n1,n2,n3))
# Counter({Person(name='John'): 2, Person(name='Jane'): 1})
8

你还需要实现 __hash__ 这个方法:

class Person(object):
    def __init__(self, name=None, address=None):
        self.name = name
        self.address = address

    def __eq__(self, other):
        return self.name == other.name and self.address == other.address

    def __hash__(self):
        return hash((self.name, self.address))

现在你的代码可以正常运行了:

>>> Counter(people)
Counter({<__main__.Person object at 0x24a7590>: 2, <__main__.Person object at 0x24a75d0>: 1})

撰写回答