How to change the behavior of a python dictionary's __setattr__?

此生再无相见时 提交于 2019-12-06 05:21:02

问题


In Python, everything has a class. Therefore dict also has a class.

So, in theory, I should be able to change the implementation of the keyvalue assignment behavior.


Example:

d = dict()
d['first'] = 3    # Internally d['first'] is stored as 6 [i.e. value*2 if value is INT]

print d['first']  # should print 6

d['second'] = 4 

print d['second'] # should print 8


I noticed that most objects have attributes listed in OBJECT.__dict__ or vars(OBJECT). But this isn’t the case for dict or list.

How can I get the desired behavior by overriding dict.__setattr__() method?


回答1:


It is __setitem__ that have to be overriden in this case - and it is as simples as:

class MyDict(dict):
    def __setitem__(self, key, value):
         dict.__setitem__(self, key, 2 * value)

Example:

>>> m  = MyDict()
>>> m[0] = 5
>>> m
{0: 10}

__setattr__ controls how object attributes themselves (not key/value pairs) are attributed.




回答2:


Be careful when subclassing dict. If you just override __setitem__, then other dict methods, such as update, will not call your __setitem__.

class MyDict(dict):
    def __setitem__(self, key, value):
         dict.__setitem__(self, key, 2 * value)

d = MyDict()
d['first'] = 3
print(d['first'])
# 6

d.update({'first':4})
print(d['first'])
# 4                       # <--- __setitem__ was not called.

In order to create a dict-like object, you either need to subclass dict and override all the methods (see OrderedDict for an example of this approach), or subclass collections.MutableMapping and override a small subset those methods (from which all the other methods are derived).

import collections

class MyDict2(collections.MutableMapping,dict):
    def __getitem__(self, key):
        return dict.__getitem__(self, key)
    def __setitem__(self, key, value):
         dict.__setitem__(self, key, 2 * value)
    def __delitem__(self, key):
        dict.__delitem__(self, key)
    def __iter__(self):
        return dict.__iter__(self)
    def __len__(self):
        return dict.__len__(self)
    def __contains__(self, x):
        return dict.__contains__(self, x)

d = MyDict2()
d['first'] = 3
print(d['first'])
# 6
d.update({'first':4})
print(d['first'])
# 8



回答3:


You can’t. The dict class is written in C a Python builtin, which means you can’t really shouldn’t do any kind of monkey-patching on it.

You can, however, inherit from it and override whatever methods you want in your subclass.




回答4:


If you run the following code in the console you will see that the dict class/object is read-only:

{}.__setitem__ = None
AttributeError: 'dict' object attribute '__setitem__' is read-only

You need to what @jsbueno posted, which is create a subclass of the dict class, override the __setitem__ method, multiply the value by two and then call the original dict __setitem__ method.



来源:https://stackoverflow.com/questions/8752451/how-to-change-the-behavior-of-a-python-dictionarys-setattr

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!