关于python:如何正确地继承dict并覆盖__getitem__&

How to properly subclass dict and override __getitem__ & __setitem__

我正在调试一些代码,我想知道何时访问特定的字典。实际上,它是一个类,子类dict并实现了一些额外的特性。无论如何,我想做的是我自己子类dict,并添加override __getitem____setitem__,以产生一些调试输出。现在,我有

1
2
3
4
5
6
7
8
9
10
11
12
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子类,并更改了对超级结构的调用。不过,似乎什么都没有发生。我以为我很聪明,但我想知道我是否应该朝着不同的方向走。

谢谢你的帮助!


子类化dict时的另一个问题是,内置__init__不调用update,内置update不调用__setitem__。因此,如果您希望所有的setitem操作通过您的__setitem__函数,您应该确保它被称为您自己:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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


你所做的应该是绝对有效的。我测试了您的类,除了日志语句中缺少左括号外,它还可以正常工作。我能想到的只有两件事。首先,日志语句的输出设置是否正确?您可能需要在脚本顶部放置一个logging.basicConfig(level=logging.DEBUG)

其次,__getitem____setitem__只在[]访问期间调用。因此,请确保您只能通过d[key]访问DictWatch,而不是通过d.get()d.set()访问。


这不会真正改变结果(对于良好的日志阈值,应该是有效的):您的初始化应该是:

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

相反,因为如果使用dictwatch([(1,2),(2,3)]或dictwatch(a=1,b=2)调用方法,这将失败。

(或者,更好的是,不要为此定义构造函数)


考虑子类化UserDictUserList。这些类被设计成子类,而正常的dictlist不是,并且包含优化。


你要做的就是

1
2
3
class BatchCollection(dict):
    def __init__(self, inpt={}):
        super(BatchCollection, self).__init__(inpt)

我个人使用的示例用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
### 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中测试