Одним из решений будет Mapping
, который проксирует основное отображение. Свойство d
обернет базовое отображение self._d
в прокси-оболочку и вернет его, и использование этого прокси-сервера будет демонстрировать необходимое поведение. Пример:
from collections.abc import Mapping
class DProxy(Mapping):
__slots__ = ('proxymap',)
def __init__(self, proxymap):
self.proxymap = proxymap
def __getitem__(self, key):
val = self.proxymap[key]
if key == 'a':
val += 2
return val
def __iter__(self):
return iter(self.proxymap)
def __len__(self):
return len(self.proxymap)
Как только вы это сделаете, ваш первоначальный класс может быть:
class Test:
def __init__(self):
self._d = {'a': 1, 'b': 2}
@property
def d(self):
return DProxy(self._d)
Пользователи будут затем обращаться к экземплярам Test
с test.d[somekey]
; test.d
вернул бы прокси, который затем изменил бы результат __getitem__
, необходимый для somekey
. Они могут даже хранить ссылки с помощью locald = test.d
, а затем использовать locald
, сохраняя при этом необходимые параметры прокси. Вы можете сделать его MutableMapping
, если необходимо, но простой прокси на основе Mapping
позволяет избежать сложности, когда целью является чтение значений, никогда не изменяя их через прокси.
Да, это создает новый экземпляр DProxy
при каждом доступе к d
; Вы можете кэшировать его, если хотите, но, учитывая, насколько прост __init__
класс DProxy
, стоимость имеет смысл, только если квалифицированный доступ через атрибут d
часто выполняется на самых горячих путях кода.