Подкласс dict __getitem__ с сохранением исходного типа класса - PullRequest
1 голос
/ 06 августа 2020

Я пытаюсь создать подкласс метода python dictionary __getitem__, сохраняя исходный тип класса при доступе к ha sh. Нет ответа в , правильно подклассифицировав или , идеальное переопределение , похоже, решило бы эту проблему. Например,

class MyDict(dict):
    def __init__(self, data=None):
        if not data:
            data = {}
        dict.__init__(self, data)

    def __getitem__(self, key):
        if key == 'b':
            print('Found "b"')

        return dict.__getitem__(self, key)

shallow_dict = MyDict(data={
    'b': 'value'
})

# prints Found "b", as expected
x = shallow_dict['b']

deep_dict = MyDict(data={
    'a': {
        'b': 'value'
    }
})

# prints nothing
x = deep_dict['a']['b']

Это происходит потому, что, когда мы обращаемся к ['b'], мы фактически обращаемся к dict, а не к MyDict. Поэтому я попытался решить эту проблему, скопировав содержимое в новый объект:

def __getitem__(self, key):
    data = dict.__getitem__(self, key)
    if key == 'b':
        print('Found "b"')

    if isinstance(data, dict):
        return MyDict(data)
    return data

Однако это решение привело к новой проблеме при записи содержимого в ha sh, потому что я возвращаю копию и не ссылка:

deep_dict['a']['b'] = 'other value'

# prints 'value'
print(deep_dict['a']['b'])

Есть какие-нибудь предложения о том, как правильно поддерживать тип, поскольку копирование имело этот побочный эффект?

1 Ответ

1 голос
/ 06 августа 2020

Как насчет вашего MyDict просто proxy для dict:

class MyDict(object):
    def __init__(self, data={}):
        self.data = data

    def __getitem__(self, key):
        if key == 'b':
            print('Found "b"')
        return MyDict(self.data.__getitem__(key))

    def __setitem__(self, key, value):
        return self.data.__setitem__(key, value)

    def __repr__(self):
        return self.data.__repr__()

    # add more __magic__ methods as you wish

shallow_dict = MyDict({
    'b': 'value'
})

x = shallow_dict['b']


deep_dict = MyDict({
    'a': {
        'b': 'value'
    }
})

x = deep_dict['a']['b']

# assignment
deep_dict['a']['a'] = {'b': 'here'}
deep_dict['a']['a']['b']
print(deep_dict)

OUTPUT:

Found "b"
Found "b"
Found "b"
{'a': {'b': 'value', 'a': {'b': 'here'}}}

Как вы можете видеть, когда получаете self.data внутри __getitem__ он просто передает результат self.data.__getitem__ по ссылке на новый объект MyDict.

...