从OrderedDict中获取元组键的键 count

2 投票
1 回答
3167 浏览
提问于 2025-04-18 18:52

我有一个字典,长得像这样:

my_dict=collections.OrderedDict([((123, 1), 'qwe'), ((232, 1), 'asd'), ((234, 2), 'zxc'), ((6745, 2), 'aaa'), ((456, 3), 'bbb')])

这个字典里的元组组合是唯一的,我想保持插入的顺序,所以我用了OrderedDict。现在这个字典里有超过一万条数据。我想高效地维护一个计数器,用来统计元组中第二个元素的数量。简单来说,我需要在每次添加或删除字典里的项目时,知道这个数量。目前我每次都要遍历一下my_dict来获取计数,这样做感觉非常耗时。

在上面的例子中,我希望输出是:

1:2 # As in 1 occurs 2 times 
2:2
3:1

现在我这样做:

from collections import OrderedDict, Counter
my_dict = OrderedDict()
my_dict[(123,1)] = 'qwe'
my_dict[(232,1)] = 'asd'
my_dict[(234,2)] = 'zxc'
my_dict[(6745,2)] = 'aaa'
my_dict[(456,3)] = 'bbb'
cnt = []
for item in my_dict.keys():
    cnt.append(item[1])
print Counter(cnt)

我不确定这样做是否是最好的方法,但有没有办法重写=运算符和pop函数,让它在每次执行这些操作时自动增加或减少计数呢?

1 个回答

4

要让一个 CounterOrderedDict 很好地配合使用,可能需要一些子类化的操作。下面是一个可能有效的例子(我只实现了 __setitem____getitem__,如果你需要更全面的实现,可以告诉我):

import collections

class CountedOrderedDict(collections.OrderedDict):
    def __init__(self, *args, **kwargs):
        self.counter = collections.Counter()
        super(CountedOrderedDict, self).__init__(*args, **kwargs)

    def __delitem__(self, key):
        super(CountedOrderedDict, self).__delitem__(key)
        self.counter[key[1]] -= 1

    def __setitem__(self, key, value):
        if key not in self:
            self.counter[key[1]] += 1

        super(CountedOrderedDict, self).__setitem__(key, value)

使用示例:

>>> my_dict = CountedOrderedDict({(123,1): 'sda', (232,1) : 'bfd', (234,2) : 'csd', (6745,2) : 'ds', (456,3) : 'rd'})
>>> my_dict.counter
Counter({'1': 2, '2': 2, '3': 1})
>>> del my_dict[(123,1)]
>>> my_dict.counter
Counter({'2': 2, '1': 1, '3': 1})
>>> my_dict[(150,1)] = "asdf"
>>> my_dict.counter
Counter({'1': 2, '2': 2, '3': 1})

这里有一个更通用的 CountedOrderedDict 实现,它接受一个键函数作为参数。

import collections

class CountedOrderedDict(collections.OrderedDict):
    def __init__(self, key=lambda k: k, *args, **kwargs):
        self.counter = collections.Counter()
        self.key_transform = key
        super(CountedOrderedDict, self).__init__(*args, **kwargs)

    def __delitem__(self, key):
        super(CountedOrderedDict, self).__delitem__(key)
        self.counter[self.key_transform(key)] -= 1

    def __setitem__(self, key, value):
        if key not in self:
            self.counter[self.key_transform(key)] += 1

        super(CountedOrderedDict, self).__setitem__(key, value)

根据你的需求,你可以这样实例化它:

my_dict = CountedOrderedDict(key=lambda k: k[1])

撰写回答