Я пытаюсь написать специализированный подкласс встроенного в Python property
, который принимает входной аргумент при декорировании функции, подобной этой:
@special_property(int)
def my_number(self):
return self._number
Я использовал примеры на https://realpython.com/primer-on-python-decorators/ в качестве ссылки, чтобы попытаться выполнить это следующим образом:
class special_property(property):
def __init__(self, property_type):
super().__init__()
self.type = property_type
def __call__(self, fn):
fn.type = self.type
return fn
Эта настройка позволяет мне получить явное type
, указанное для свойства в классе, который использует мой special_property
, например:
class Object(object):
def __init__(self):
super().__init__()
self._number = 0
@special_property(int)
def my_number(self):
return self._number
def load_from_json(self, json_file):
with open(json_file, 'r') as f:
state = json.load(f)
for name, value in state.items():
if hasattr(self, name):
klass = self.__class__.__dict__[name].type
try:
self.__setattr__(name, klass(value))
except:
ValueError('Error loading from JSON')
Причина, по которой я это делаю, состоит в том, чтобы сделать возможным создание сериализуемого класса JSON путем декорирования свойств, которые должны храниться / загружаться в файл JSON. В этом примере не нужно было бы явно гарантировать, что тип my_number
является int
, потому что модуль json
может обрабатывать это автоматически. Но в моем случае есть более сложные объекты, которые я отмечаю как сериализуемые JSON с помощью декоратора и реализующие собственные методы сериализации / десериализации. Однако, чтобы это работало, код должен знать, какой тип ожидается для свойств.
Это позволяет мне, например, создавать иерархии сериализуемых классов JSON. Моя текущая реализация позволяет хранить и загружать всю структуру данных из JSON без потери какой-либо информации.
Теперь я хочу сделать еще один шаг и сделать возможным проверку формата данных также при попытке установить значение specialized_property
. Следовательно, я хочу быть в состоянии сделать это:
@specialized_property(int)
def my_number(self):
return self._number
@my_number.setter
def my_number(self, value):
if value < 0:
raise ValueError('Value of `my_number` should be >= 0')
self._number = value
Это позволило бы мне, например, убедиться, что список чисел, загруженных из файла JSON, имеет правильный размер.
Однако из-за кода, добавляющего аргумент property_type
, теперь невозможно использовать @my_number.setter
. Если я пытаюсь запустить код, я получаю:
AttributeError: 'function' object has no attribute 'setter'
Это имеет смысл для меня, потому что переопределяет метод __call__
и возвращает объект function
. Но как мне обойти это и выполнить то, что я хочу?