Ошибка печати коллекции. Экземпляры экземпляра

1

Объекты-счетчики являются подклассами 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:\Python32\lib\collections.py", line 586, in __repr__
    items = ', '.join(map('%r: %r'.__mod__, self.most_common()))
  File "C:\Python32\lib\collections.py", line 477, in most_common
    return sorted(self.items(), key=_itemgetter(1), reverse=True)
TypeError: unorderable types: NoneType() < int()
>>> 

Это ошибка?. Не следует ли давать c.setdefault('castles') ошибку value/key вместо молчания принимать ключ без значения? Или, может быть, метод перечня с учетом значений None?

Теги:
python-3.x

2 ответа

2
Лучший ответ

Да, похоже на ошибку. Проблема в том, что setdefault без аргумента value предполагает, что значение равно None, в то время как в случае с Counter оно должно действительно вставить либо одно, либо ноль или сбой, вызвав некоторое исключение.

В Python 2.7 ваш фрагмент работает, кстати, хотя он все еще вставляет значение None, нарушая инварианты Counter.

Имейте в виду, что это не первый дефект ошибки/дизайна, с которым я сталкиваюсь с collections.Counter.

  • 0
    Счетчик был backport для Python 2.7? Это странно, это работает в 2.7 и терпит неудачу в py3k.
  • 0
    @joaquin: хорошо, это терпит неудачу в 3.2. Я не проверял 3.0 или 3.1.
Показать ещё 6 комментариев
2

Линия 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})
  • 0
    Спасибо, Рэймонд. Я просто играл с ним как со словарем, не кодируя ничего реального, просто предполагая, что этот объект "плохо себя ведет". Это словарь подкласса. Я могу сделать это с помощью обычного словаря, он помещает None, когда значение не указано, но когда я его печатаю, я не получаю исключения.
  • 0
    @joaquin Это не "плохое поведение". Вы не используете его по назначению и, следовательно, получаете ошибку. Только не делай этого, и все будет хорошо.
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню