Как правильно подкласс dict и переопределить getitem & setitem


Я отлаживаю некоторый код, и я хочу узнать, когда конкретный словарь доступен. Ну, это на самом деле класс, подкласс dict и реализует несколько дополнительных функций. Во всяком случае, то, что я хотел бы сделать, это подкласс dict себя и добавить override __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 и изменил вызов суперконструктора. И все же, кажется, ничего не происходит. Я думал, что поступаю умно, но мне кажется, что я должен идти в другом направлении.

Спасибо за помощь!

5 61

5 ответов:

то, что вы делаете, должно абсолютно работать. Я проверил ваш класс, и помимо отсутствующей открывающей скобки в ваших заявлениях журнала, он работает просто отлично. Есть только две вещи, о которых я могу думать. Во-первых, правильно ли установлен вывод вашего оператора log? Возможно, вам придется поставить logging.basicConfig(level=logging.DEBUG) в верхней части вашего скрипта.

второе, __getitem__ и __setitem__ вызываются только во время [] обращается. Поэтому убедитесь, что вы только доступ DictWatch через d[key], а не d.get() и d.set()

еще одна проблема при создании подклассов 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

это не должно действительно изменить результат (который должен работать, для хороших пороговых значений регистрации) : ваш init должно быть :

def __init__(self,*args,**kwargs) : dict.__init__(self,*args,**kwargs) 

вместо этого, потому что если вы вызываете свой метод с помощью DictWatch([(1,2),(2,3)]) или диктофон (a=1,b=2) это не удастся.

(или, лучше, не определяйте конструктор для этого)

рассмотрим подклассы UserDict или UserList. Эти классы предназначены для подклассов, тогда как нормальный dict и list не содержат оптимизаций.

все, что вам нужно сделать, это

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