Разделение на подклассы словаря Python для переопределения __ setitem __

Я создаю класс, который разделяет на подклассы dict, и переопределения __setitem__. Я хотел бы быть уверенным, что мой метод назовут во всех экземплярах, где объекты словаря могли возможно быть установлены.

Я обнаружил три ситуации, где Python (в этом случае, 2.6.4) не называет мой переопределенный __setitem__ метод при устанавливании значений, и вместо этого звонит PyDict_SetItem непосредственно

  1. В конструкторе
  2. В setdefault метод
  3. В update метод

Как очень простой тест:

class MyDict(dict):
    def __setitem__(self, key, value):
        print "Here"
        super(MyDict, self).__setitem__(key, str(value).upper())

>>> a = MyDict(abc=123)
>>> a['def'] = 234
Here
>>> a.update({'ghi': 345})
>>> a.setdefault('jkl', 456)
456
>>> print a
{'jkl': 456, 'abc': 123, 'ghi': 345, 'def': '234'}

Вы видите, что переопределенный метод только называют при установке объектов явно. Заставить Python всегда называть мой __setitem__ метод, я должен был повторно реализовать те три метода, как это:

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

    def __setitem__(self, key, value):
        print "Here"
        super(MyUpdateDict, self).__setitem__(key, value)

    def update(self, *args, **kwargs):
        if args:
            if len(args) > 1:
                raise TypeError("update expected at most 1 arguments, got %d" % len(args))
            other = dict(args[0])
            for key in other:
                self[key] = other[key]
        for key in kwargs:
            self[key] = kwargs[key]

    def setdefault(self, key, value=None):
        if key not in self:
            self[key] = value
        return self[key]

Есть ли любые другие методы, которые я должен переопределить, чтобы знать, что Python будет всегда называть мой __setitem__ метод?

ОБНОВЛЕНИЕ

На предложение gs я попытался разделить UserDict на подклассы (на самом деле, IterableUserDict, так как я хочу выполнить итерации по ключам) как это:

from UserDict import *;
class MyUserDict(IterableUserDict):
    def __init__(self, *args, **kwargs):
        UserDict.__init__(self,*args,**kwargs)

    def __setitem__(self, key, value):
        print "Here"
        UserDict.__setitem__(self,key, value)

Этот класс, кажется, правильно называет мой __setitem__ на setdefault, но это не обращается к нему update, или когда исходные данные предоставляются конструктору.

ОБНОВЛЕНИЕ 2

Предложение Peter Hansen заставило меня более тщательно смотреть на dictobject.c, и я понял, что метод обновления мог быть упрощен немного, так как встроенный конструктор словаря просто называет встроенный метод обновления так или иначе. Это теперь похоже на это:

def update(self, *args, **kwargs):
    if len(args) > 1:
        raise TypeError("update expected at most 1 arguments, got %d" % len(args))
    other = dict(*args, **kwargs)
    for key in other:
        self[key] = other[key]
42
задан Ian Clelland 14 January 2010 в 16:27
поделиться

2 ответа

Я отвечаю на свой вопрос, так как в конце концов решил, что я действительно действительно хочу создать подкласс Dict, а не создавать новый класс сопоставления, и UserDict в некоторых случаях по-прежнему подчиняется базовому объекту Dict. вместо использования предоставленного __ setitem __ .

После прочтения и перечитывания исходного кода Python 2.6.4 (в основном Objects / dictobject.c , но я поискал в других местах, чтобы увидеть, где используются различные методы), я понял, что следующие code является достаточным, чтобы мой __setitem__ вызывался каждый раз при изменении объекта, а в остальном он вел себя точно так же, как Python Dict:

Предложение Питера Хансена заставило меня более внимательно взглянуть на dictobject. c , и я понял, что метод обновления в моем исходном ответе можно немного упростить, поскольку встроенный конструктор словаря в любом случае просто вызывает встроенный метод обновления. Итак, второе обновление в моем ответе было добавлено в приведенный ниже код (каким-то полезным человеком ;-).

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

    def __setitem__(self, key, value):
        # optional processing here
        super(MyUpdateDict, self).__setitem__(key, value)

    def update(self, *args, **kwargs):
        if args:
            if len(args) > 1:
                raise TypeError("update expected at most 1 arguments, "
                                "got %d" % len(args))
            other = dict(args[0])
            for key in other:
                self[key] = other[key]
        for key in kwargs:
            self[key] = kwargs[key]

    def setdefault(self, key, value=None):
        if key not in self:
            self[key] = value
        return self[key]

Я тестировал это с помощью этого кода:

def test_updates(dictish):
    dictish['abc'] = 123
    dictish.update({'def': 234})
    dictish.update(red=1, blue=2)
    dictish.update([('orange', 3), ('green',4)])
    dictish.update({'hello': 'kitty'}, black='white')
    dictish.update({'yellow': 5}, yellow=6)
    dictish.setdefault('brown',7)
    dictish.setdefault('pink')
    try:
        dictish.update({'gold': 8}, [('purple', 9)], silver=10)
    except TypeError:
        pass
    else:
        raise RunTimeException("Error did not occur as planned")

python_dict = dict([('b',2),('c',3)],a=1)
test_updates(python_dict)

my_dict = MyUpdateDict([('b',2),('c',3)],a=1)
test_updates(my_dict)

, и он прошел. Все другие реализации, которые я пробовал, в какой-то момент потерпели неудачу. Я все равно приму любые ответы, которые покажут мне, что я что-то пропустил, но в противном случае через пару дней я поставлю галочку рядом с этим и назову это правильным ответом :)

{{1} }
48
ответ дан 26 November 2019 в 23:55
поделиться

Каков ваш пример использования для разделения на подклассы dict?

Вы не должны делать этого для реализации подобного dict объекта, и могло бы быть более просто в случае записать обычный класс, затем добавить поддержку необходимого подмножества интерфейса dict.

лучшим способом выполнить, что вы после, является, вероятно, абстрактный базовый класс MutableMapping. PEP 3119 - Представляющий Абстрактные базовые классы

Это также поможет вам anser, вопросом "Являются там какие-либо другие методы, которые я должен переопределить?". Необходимо будет переопределить все абстрактные методы. Для MutableMapping: Абстрактные методы включают setitem, delitem. Конкретные методы включают поп, popitem, ясный, обновление.

4
ответ дан 26 November 2019 в 23:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: