添加计数器会删除密钥

时间:2014-02-19 16:56:24

标签: python data-structures dictionary counter multiset

见下文,为什么+=的实施会破坏原始计数器中的密钥?

>>> c = Counter({'a': 0, 'b': 0, 'c': 0})
>>> c.items()
[('a', 0), ('c', 0), ('b', 0)]
>>> c += Counter('abba')
>>> c.items()
[('a', 2), ('b', 2)]

我认为至少可以说这是不礼貌的,“X被计算0次”和“我们甚至没有计算Xs”之间存在很大差异。似乎collections.Counter根本不是反制,它更像是一个多重集。

但是计数器是dict 的子类,我们允许用零值或负值构造它们:Counter(a=0, b=-1)。如果它实际上是“一包东西”,这不会被禁止,限制init接受可迭代的可迭代物品吗?

为了进一步混淆问题,反对实施updatesubtract方法,这些方法对+-运算符有不同的行为。看来这堂课正在发生身份危机!

反击是一个字典还是一个包?

2 个答案:

答案 0 :(得分:9)

Counter 是一种多重集合。来自Counter() documentation

  

提供了几种数学运算,用于组合Counter个对象以生成多重集合(计数大于零的计数器)。加法和减法通过添加或减去相应元素的计数来组合计数器。交点和并集返回相应计数的最小值和最大值。每个操作都可以接受带符号计数的输入,但输出将排除计数为零或更少的结果

强调我的。

进一步说明,它会为您提供有关Counter的多重性质的更多详细信息:

  

注意:计数器主要用于处理正整数以表示运行计数;但是,注意不要不必要地排除需要其他类型或负值的用例。为帮助处理这些用例,本节介绍了最小范围和类型限制。

     

[...]

     
      
  • multiset方法仅适用于具有正值的用例。输入可以是负数或零,但仅创建具有正值的输出。没有类型限制,但值类型需要支持加法,减法和比较。
  •   

所以Counter个对象 ;词典行李。但是,标准词典不支持添加,但是Counter会这样做,因此Counter不会破坏字典设置的优先级。

如果您想保留零,请使用Counter.update()并传入另一个对象的Counter.elements()结果:

c.update(Counter('abba').elements())

演示:

>>> c = Counter({'a': 0, 'b': 0, 'c': 0})
>>> c.update(Counter('abba').elements())
>>> c
Counter({'a': 2, 'b': 2, 'c': 0})

答案 1 :(得分:8)

来自source;

def __add__(self, other):
    '''Add counts from two counters.

    >>> Counter('abbb') + Counter('bcc')
    Counter({'b': 4, 'c': 2, 'a': 1})

    '''
    if not isinstance(other, Counter):
        return NotImplemented
    result = Counter()
    for elem, count in self.items():
        newcount = count + other[elem]
        if newcount > 0:
            result[elem] = newcount
    for elem, count in other.items():
        if elem not in self and count > 0:
            result[elem] = count
    return result

似乎Counter实现为删除键,其总和为零非正键。由于默认值为零,并且源也为零,因此生成的dict不包含该键。

也许你可以通过更新获得相同的行为:

a.update(b)

似乎做你想做的事。可能比较慢,__add__方法的手工实现会快得多。