Как изменить поведение __setattr__ словаря Python? - PullRequest
4 голосов
/ 06 января 2012

В Python у всего есть класс.Поэтому dict также имеет класс.

Итак, теоретически я должен иметь возможность изменить реализацию поведения назначения значения ключа.


Пример:

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


Я заметил, что большинство объектов имеют атрибуты, перечисленные в OBJECT.__dict__ или vars(OBJECT).Но это не относится к dict или list.

Как получить желаемое поведение, переопределив метод dict.__setattr__()?

Ответы [ 4 ]

11 голосов
/ 06 января 2012

Это __setitem__, которые должны быть переопределены в этом случае - и это так просто, как:

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

Пример:

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

__setattr__ контролирует, как присваиваются сами атрибуты объекта (не пары ключ / значение).

8 голосов
/ 06 января 2012

Будьте осторожны при создании подкласса dict. Если вы просто переопределите __setitem__, то другие dict методы, такие как update, не будут вызывать ваш __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.

Для создания объекта, похожего на dict, вам необходимо либо создать подкласс dict и переопределить все методы (пример такого подхода см. OrderedDict ), либо подкласс collections.MutableMapping и переопределить небольшое подмножество этих методов (из которых получены все остальные методы).

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
2 голосов
/ 06 января 2012

Вы не можете. Класс dict написан на на C , встроенном в Python, что означает, что вы не можете действительно не делать никаких обезьян-патчей на нем.

Однако вы можете наследовать его и переопределять любые методы, которые вы хотите использовать в своем подклассе.

1 голос
/ 06 января 2012

Если вы запустите следующий код в консоли, вы увидите, что класс / объект dict доступен только для чтения:

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

Вам необходимо то, что @jsbueno опубликовал, то есть создать подкласс классакласс dict, переопределите метод __setitem__, умножьте значение на два и затем вызовите оригинальный метод dict __setitem__.

...