Как правильно подклассировать dict и переопределить __getitem__ & __setitem__
Я отлаживаю некоторый код, и я хочу узнать, когда доступ к конкретному словарю. Ну, это на самом деле класс подкласса dict
и реализует несколько дополнительных функций. Во всяком случае, то, что я хотел бы сделать, - это подкласс dict
сам и добавить переопределение __getitem__
и __setitem__
для создания некоторого отладочного вывода. Прямо сейчас у меня есть
class DictWatch(dict):
def __init__(self, *args):
dict.__init__(self, args)
def __getitem__(self, key):
val = dict.__getitem__(self, key)
log.info("GET %s['%s'] = %s" % str(dict.get(self, 'name_label')), str(key), str(val)))
return val
def __setitem__(self, key, val):
log.info("SET %s['%s'] = %s" % str(dict.get(self, 'name_label')), str(key), str(val)))
dict.__setitem__(self, key, val)
'name_label'
- это ключ, который в конечном итоге будет установлен, который я хочу использовать для идентификации вывода. Затем я изменил класс, который я использую для подкласса DictWatch
вместо dict
и изменил вызов суперконструктора. Тем не менее, похоже, ничего не происходит. Я думал, что я умный, но я задаюсь вопросом, должен ли я идти в другом направлении.
Спасибо за помощь!
Ответы
Ответ 1
То, что вы делаете, должно абсолютно работать. Я тестировал ваш класс, и помимо отсутствующих открывающих круглых скобок в ваших операторах журнала, он работает отлично. Есть только две вещи, о которых я могу думать. Во-первых, правильно ли выводится вывод вашего оператора журнала? Возможно, вам нужно будет положить logging.basicConfig(level=logging.DEBUG)
вверху вашего script.
Во-вторых, __getitem__
и __setitem__
вызываются только при обращении []
. Поэтому убедитесь, что вы используете только DictWatch
через d[key]
, а не d.get()
и d.set()
Ответ 2
Другая проблема, когда подклассификация dict
заключается в том, что встроенный __init__
не вызывает update
, а встроенный update
не вызывает __setitem__
. Итак, если вы хотите, чтобы все операции setitem проходили через вашу функцию __setitem__
, вы должны убедиться, что она сама вызвана:
class DictWatch(dict):
def __init__(self, *args, **kwargs):
self.update(*args, **kwargs)
def __getitem__(self, key):
val = dict.__getitem__(self, key)
print 'GET', key
return val
def __setitem__(self, key, val):
print 'SET', key, val
dict.__setitem__(self, key, val)
def __repr__(self):
dictrepr = dict.__repr__(self)
return '%s(%s)' % (type(self).__name__, dictrepr)
def update(self, *args, **kwargs):
print 'update', args, kwargs
for k, v in dict(*args, **kwargs).iteritems():
self[k] = v
Ответ 3
Это не должно действительно изменить результат (который должен работать, для хороших пороговых значений регистрации):
ваш init должен быть:
def __init__(self,*args,**kwargs) : dict.__init__(self,*args,**kwargs)
потому что если вы вызовете свой метод с помощью DictWatch ([(1,2), (2,3)]) или DictWatch (a = 1, b = 2), это не удастся.
(или, лучше, не определяйте для этого конструктор)
Ответ 4
Рассмотрим подкласс UserDict
или UserList
. Эти классы предназначены для подкласса, тогда как нормальные dict
и list
не являются и содержат оптимизацию.
Ответ 5
Все, что вам нужно сделать, это
class BatchCollection(dict):
def __init__(self, inpt={}):
super(BatchCollection, self).__init__(inpt)
Пример использования для личного использования
### EXAMPLE
class BatchCollection(dict):
def __init__(self, inpt={}):
super(BatchCollection, self).__init__(inpt)
def __setitem__(self, key, item):
if (isinstance(key, tuple) and len(key) == 2
and isinstance(item, collections.Iterable)):
# self.__dict__[key] = item
super(BatchCollection, self).__setitem__(key, item)
else:
raise Exception(
"Valid key should be a tuple (database_name, table_name) "
"and value should be iterable")
Примечание: проверено только на python3