Несколько раз (даже несколько подряд) я был укушен defaultdict ошибкой: упущение, что что-то - на самом деле defaultdict и обработка ее как регулярный словарь.
d = defaultdict(list)
...
try:
v = d["key"]
except KeyError:
print "Sorry, no dice!"
Для тех, кто был укушен также, проблема очевидна: когда d не имеет никакого ключевого 'ключа', v = d["key"]
волшебно создает пустой список и присваивает его обоим d["key"]
и v
вместо того, чтобы повысить исключение. Который может быть настоящей болью, чтобы разыскать, если d прибывает из некоторого модуля, детали которого каждый не помнит очень хорошо.
Я ищу способ снизить остроту этой ошибки. Для меня лучшее решение состояло бы в том, чтобы так или иначе отключить волшебство defaultdict прежде, чем возвратить его клиенту.
Вы можете предотвратить создание значений по умолчанию, назначив d.default_factory = None
. Однако мне не очень нравится идея о внезапном изменении поведения объекта. Я бы предпочел копировать значения в новый dict
, если это не налагает серьезного снижения производительности.
использовать другую идиому:
if 'key' not in d:
print "Sorry, no dice!"
Это именно то поведение, которое вы хотите от defaultdict
, а не от ошибки . Если вам это не нужно, не используйте defaultdict.
Если вы все время забываете, какой тип имеют переменные, то назовите их соответствующим образом - например, суффикс ваших имен defaultdict с "_ddict".
Используя идею rkhayrov о сбросе self.default_factory
, вот переключаемый подкласс defaultdict
:
class ToggleableDefaultdict(collections.defaultdict):
def __init__(self,default_factory):
self._default_factory=default_factory
super(ToggleableDefaultdict,self).__init__(default_factory)
def off(self):
self.default_factory=None
def on(self):
self.default_factory=self._default_factory
Например:
d=ToggleableDefaultdict(list)
d['key'].append(1)
print(d)
# defaultdict(<type 'list'>, {'key': [1]})
d.off()
d['newkey'].append(2)
# KeyError: 'newkey'
d.on()
d['newkey'].append(2)
# defaultdict(<type 'list'>, {'newkey': [2], 'key': [1]})
Вы все еще можете преобразовать его в обычный dict.
d = collections.defaultdict(list)
d = dict(d)