Python 如何正确地将dict子类化并覆盖_ugetItem__设定项__

Python 如何正确地将dict子类化并覆盖_ugetItem__设定项__,python,dictionary,Python,Dictionary,我正在调试一些代码,我想知道何时访问某个特定的字典。实际上,它是一个类,它是dict的子类,实现了一些额外的特性。无论如何,我想做的是子类dict我自己并添加override\uuuu getitem\uuuuuu和\uuuu setitem\uuuuu,以生成一些调试输出。现在,我有 class DictWatch(dict): def __init__(self, *args): dict.__init__(self, args) def __getitem

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

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\u label”
是一个最终将被设置为标识输出的键。然后,我将插入指令的类更改为子类
DictWatch
,而不是
dict
,并更改了对超级构造函数的调用。不过,似乎什么也没有发生。我以为我很聪明,但我不知道我是否应该走另一条路


谢谢你的帮助

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


其次,
\uuuuu getitem\uuuuuuuu
\uuuuu setitem\uuuuuuuuuu
仅在
[]
访问期间调用。因此,请确保您仅通过
d[key]
访问
DictWatch
,而不是
d.get()
d.set()

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

def __init__(self,*args,**kwargs) : dict.__init__(self,*args,**kwargs) 
相反,因为如果您使用DictWatch([(1,2),(2,3)]或DictWatch(a=1,b=2)调用您的方法,这将失败


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

子类化
dict
时的另一个问题是,内置的
\uuuu init\uuuu
不调用
update
,而内置的
update
不调用
\uuu setitem\uuuu
。因此,如果您希望所有setitem操作都通过
\uuuuu setitem\uuuuu
函数,您应该确保自己调用它:

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

你所要做的就是

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

考虑子类化
UserDict
UserList
。这些类旨在被子类化,而普通的
dict
list
则不是,并且包含优化。

正如andrew pate answer建议的那样,子类化
集合。UserDict
而不是
dict
更不容易出错

下面是一个简单地继承
dict
时出现问题的示例:

类MyDict(dict):
定义设置项(自身、键、值):
超级()
d=MyDict(a=1,b=2)#糟糕!MyDict.\uuuu setitem\uuuu未调用
d、 更新(c=3)#糟糕!MyDict.\uuuu setitem\uuuu未调用
好!
印刷体(d){'a':1,'b':2,'c':3,'d':40}
UserDict
继承自
collections.abc.MutableMapping
,因此工作正常:

类MyDict(collections.UserDict):
定义设置项(自身、键、值):
超级()
d=MyDict(a=1,b=2)#好:MyDict.u_usetitem_uu_u_u正确调用
d、 更新(c=3)#良好:MyDict.u_usetitem_uu_u_u正确调用
好的
印刷体(d){'a':10,'b':20,'c':30,'d':40}
类似地,您只需实现
\uuu getitem\uuuuuuuuuuuuu
即可自动与my_dict中的
键兼容,
my_dict.get


注意:
UserDict
不是
dict
的子类,因此
isinstance(UserDict(),dict)
将失败(但是
isinstance(UserDict(),collections.abc.MutableMapping)
是否尝试使用打印而不是日志?另外,你能解释一下如何创建/配置你的日志吗?
dict.\uu init\uuu
take
*args
?看起来有点像装饰师的好人选。实际上,这不是额外的参数,而是
(str(dict.get(self,'name\u label'))、str(key)、str(val))
真的。对于OP:为了将来的参考,您可以简单地执行log.info(“%s%s%s',a,b,c),而不是Python字符串格式运算符。日志级别最终成为问题。我正在调试其他人的代码,而我最初是在另一个文件中进行测试,该文件位于不同级别的调试集中。谢谢我只担心
dict[key]
访问形式,所以这不是问题。如果您使用的是Python 3,您需要更改此示例,以便
print
print()
函数,
update()
方法使用
items()
而不是
items()
。我已经尝试了您的解决方案,但它似乎只适用于一个级别的索引(即dict[key]而不是dict[key1][key2]…)*d[key1]返回一些东西,可能是字典。第二个关键点是索引。除非返回的东西也支持监视行为,否则此技术无法工作。@AndrewNaguib:为什么它要与嵌套数组一起工作?嵌套数组也不能与普通的python dict一起工作(如果您自己没有实现的话)@AndrewNaguib:
\uuuu getitem\uuuu
需要测试
val
,并且只能有条件地这样做-例如,如果isinstance(val,dict):…
作为参考,python 3.6中的“对这个类的需求已经被直接从dict生成子类的能力部分取代;但是,这个类更容易使用,因为底层字典可以作为属性访问。@andrew举个例子可能会有帮助。@VasanthaGaneshK