如何正确地继承dict并覆盖__getitem__& __setitem__

时间:2010-03-06 00:24:34

标签: python dictionary

我正在调试一些代码,我想知道何时访问特定的字典。好吧,它实际上是一个子类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,并将调用更改为超级构造函数。但是,似乎没有任何事情发生。我以为我很聪明,但我想知道我是否应该走另一个方向。

感谢您的帮助!

6 个答案:

答案 0 :(得分:65)

子类化dict时的另一个问题是内置__init__没有调用update,而内置update没有调用{{1} }}。因此,如果您希望所有setitem操作都通过__setitem__函数,则应确保自己调用它:

__setitem__

答案 1 :(得分:31)

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

其次,__getitem____setitem__仅在[]次访问期间调用。因此,请务必仅通过DictWatch访问d[key],而不是d.get()d.set()

答案 2 :(得分:9)

这不应该真正改变结果(对于良好的记录阈值,这应该有效): 你的 init 应该是:

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

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

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

答案 3 :(得分:7)

考虑继承UserDictUserList。这些类旨在进行子类化,而普通dictlist不是,并且包含优化。

答案 4 :(得分:1)

你所要做的只是

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中测试

答案 5 :(得分:0)

要完成安德鲁·帕特的答案,下面的示例显示了dictUserDict之间的区别:

正确地覆盖字典是很困难的:

class MyDict(dict):

  def __setitem__(self, key, value):
    super().__setitem__(key, value * 10)


d = MyDict(a=1, b=2)  # Bad! MyDict.__setitem__ not called
d.update(c=3)  # Bad! MyDict.__setitem__ not called
d['d'] = 4  # Good!
print(d)  # {'a': 1, 'b': 2, 'c': 3, 'd': 40}

UserDict继承自collections.abc.MutableMapping,因此更易于自定义:

class MyDict(collections.UserDict):

  def __setitem__(self, key, value):
    super().__setitem__(key, value * 10)


d = MyDict(a=1, b=2)  # Good: MyDict.__setitem__ correctly called
d.update(c=3)  # Good: MyDict.__setitem__ correctly called
d['d'] = 4  # Good
print(d)  # {'a': 10, 'b': 20, 'c': 30, 'd': 40}

类似地,您只需实现__getitem__即可自动与key in my_dictmy_dict.get,...

兼容

注意:UserDict不是dict的子类,因此isinstance(UserDict(), dict)将失败(但isinstance(UserDict(), collections.abc.MutableMapping)将起作用)