计数器对象是dict的子类,所以它们有setdefault方法。
>>> from collections import Counter
>>> c = Counter(houses=5)
>>> print(c.setdefault.__doc__)
D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D
如果我这样做:
>>> c.setdefault('castles')
>>> c.keys()
dict_keys(['castles', 'houses'])
>>> type(c)
<class 'collections.Counter'>
一切似乎都很好。但是:
>>> c
Traceback (most recent call last):
File "<pyshell#17>", line 1, in <module>
c
File "C:Python32libcollections.py", line 586, in __repr__
items = ', '.join(map('%r: %r'.__mod__, self.most_common()))
File "C:Python32libcollections.py", line 477, in most_common
return sorted(self.items(), key=_itemgetter(1), reverse=True)
TypeError: unorderable types: NoneType() < int()
>>>
这是一个bug吗?不应该给c.setdefault('castles')
一个值/键错误,而不是默默地接受一个没有值的键?或者考虑到None值的报告方法?
是的,看起来像个bug。问题是,没有值参数的setdefault
假设值是None
,而在Counter
的情况下,它应该真正插入1或0,或者通过引发一些异常而失败。
None
值,违反了Counter
的不变量。
请注意,这不是我在collections.Counter
中遇到的第一个错误/设计缺陷。
c.setdefault('castles')
行直接赋值给c['castles'] = None
。这可能不是你想要的。
如果你想让城堡在__repr__中显示,使用c['castles'] = 0
代替。
对于计数器, 键可以是任何你想计数的值需要是一个数字。如您所见,__repr__中的排序步骤期望值都是数字,如果其中一个值被设置为None,它将不起作用。
setdefault似乎可以用来给计数器默认值或分配一个工厂函数,但这不是setdefault所做的。您根本不需要执行该步骤,因为Counter对象会自动为您返回默认值零。不需要额外的工作。这是如何工作的,简单而容易:
>>> from collections import Counter
>>> c = Counter(houses=5)
>>> c
Counter({'houses': 5})
>>> c['castles'] # counters automatically return zero for missing items, no work required
0
>>> c # but missing items won't show in the __repr__
Counter({'houses': 5})
>>> c['castles'] = 0 # unless you specifically add an entry for them
>>> c
Counter({'houses': 5, 'castles': 0})