问题描述:
我需要 Python 中的类似包/多集的数据类型。我了解 collections.Counter 经常用于此目的。但是比较运算符似乎不起作用:
- In [1]: from collections import Counter
-
- In [2]: bag1 = Counter(a=1, b=2, c=3)
-
- In [3]: bag2 = Counter(a=2, b=2)
-
- In [4]: bag1 > bag2
- Out[4]: True
这对我来说似乎是一个错误。我希望小于和大于运算符执行类似集合的子集和超集比较。但如果是这种情况,那么bag1 > bag2将是错误的,因为bag2包含一个额外的'a'. Counter 对象上似乎也没有子集/超集方法。所以我有两个问题:
Counter 对象使用什么比较逻辑?
如何比较 Counter 对象的子集、超集、真子集和真超集?
解决思路一:
我们可以像这样实现“多集包含”检查,而不是使用all迭代Counter值:
- from collections import Counter
- from functools import total_ordering
-
- class PartiallyOrderedCounter(Counter):
- def __le__(self, other):
- """ Multiset inclusion """
- test = self.copy()
- test.subtract(other)
- return not -test
-
- def __lt__(self, other):
- """ Multiset strict inclusion """
- return self <= other and self != other
这可能效率较低,但足够有趣,值得一提。
它是如何工作的:Counter一元否定(以及二元-运算符)的实现否定了结果中的每个计数器值,但随后也去除了所有负数或零。同时,该.subtract方法在原地运行(需要复制),但允许结果为负数。因此,当我们对减法的结果求反时,我们Counter只得到一个“缺失”的值other(创建被否定为正数的负数)。当且仅当没有这样的值时,计数器对象是 Falsey,我们True通过应用not逻辑运算符返回。
解决思路二:
这个悬而未决的问题很有趣:
如何比较 Counter 对象的子集、超集、真子集和真超集?
通过定义缺少的“丰富的比较方法”。您也可以使用自由函数,这将使客户端代码更加明确。
- from collections import Counter
-
- class PartiallyOrderedCounter(Counter):
-
- def __le__(self, other):
- """ Multiset inclusion """
- return all( v <= other[k] for k,v in self.items() )
-
-
- def __lt__(self, other):
- """ Multiset strict inclusion """
- return self <= other and self != other
-
-
- # TODO : __ge__ and __gt__
- # Beware : they CANNOT be written in terms of __le__ or __lt__
-
-
- a = PartiallyOrderedCounter('abc')
- b = PartiallyOrderedCounter('ab')
- c = PartiallyOrderedCounter('abe')
-
- assert a <= a
- assert not a < a
- assert b <= a
- assert b < a
- assert not a < b
- assert not c <= a
- assert not a <= c
解决思路三:
以上仅为部分解决思路,添加下方公众号后回复001,即可查看全部内容。公众号有许多评分最高的编程书籍和其它实用工具,无套路,可放心使用
如果您觉得有帮助,可以关注公众号——立志于成为对程序员有益的公众号