Как я могу сделать defaultdict безопасным для неопытных клиентов?
Несколько раз (даже несколько строк) я был укушен ошибкой defaultdict: забыв, что что-то действительно является дефолтом и рассматривает его как обычный словарь.
d = defaultdict(list)
...
try:
v = d["key"]
except KeyError:
print "Sorry, no dice!"
Для тех, кто был укушен, проблема очевидна: когда d не имеет ключевого "ключа", v = d["key"]
волшебным образом создает пустой список и назначает его как d["key"]
, так и v
вместо того, чтобы поднимать исключение. Который может быть довольно больно отслеживать, если d приходит из какого-то модуля, детали которого не помнят очень хорошо.
Я ищу способ избавиться от этой ошибки. Для меня лучшим решением было бы как-то отключить мануал defaultdict, прежде чем вернуть его клиенту.
Ответы
Ответ 1
Вы можете предотвратить создание значений по умолчанию, назначив d.default_factory = None
. Однако мне не совсем нравится идея внезапного изменения поведения объекта. Я предпочел бы копировать значения в новый dict
, если он не налагает серьезное ограничение производительности.
Ответ 2
Вы все равно можете преобразовать его в нормальный dict.
d = collections.defaultdict(list)
d = dict(d)
Ответ 3
используйте другую идиому:
if 'key' not in d:
print "Sorry, no dice!"
Ответ 4
Это именно то поведение, которое вы хотите от defaultdict
, а не ошибка. Если вы этого не хотите, не используйте defaultdict.
Если вы продолжаете забывать, что такое переменные типа, тогда назовите их соответствующим образом - например, суффикс ваших имен defaultdict с помощью "_ddict".
Ответ 5
Используя идею 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]})