Я сослался на документы python здесь, чтобы увидеть, как создать свойство класса в python, которое имеет контролируемые методы получения и установки. https://docs.python.org/3/howto/descriptor.html#id6
Я создал класс свойств дескриптора с помощью этого кода:
class PropNumberString(object):
def __init__(self, initval=None, name='var_int'):
self.val = initval
self.name = name
def __get__(self, obj, objtype):
print("in getter")
return self.val
def __set__(self, obj, val):
print("in setter")
self.val = int(val)
def __add__(self, val):
print("adding")
print("val to add: ", val)
self.val = int(self.val) + int(val)
return self
def __radd__(self, val):
return self.__add__(val)
class ConnectionTemplate(object):
tid = PropNumberString(0, "ID")
Я также попытался определить метод добавления, предполагая, что он всегда добавляет целое число, даже если предоставляется строка. Это не работает, кажется, что вызывается "__get__", а затем вызывается метод добавления возвращаемого целого числа вместо моего метода добавления.
Вот тест для демонстрации:
my_template = ConnectionTemplate()
print("Getting value")
print(my_template.tid)
print("Updating value")
my_template.tid = 100
print("Doing add")
my_template.tid = my_template.tid + "1"
print(my_template.tid)
print("type: ", type(my_template.tid))
Выход:
Getting value
in getter
0
Updating value
in setter
Doing add
in getter
Traceback (most recent call last):
File "connection_template.py", line 53, in <module>
my_template.tid = my_template.tid + "1"
TypeError: unsupported operand type(s) for +: 'int' and 'str'
Есть ли хорошее решение для этого?
Обновлено: Это то, что я в итоге сделал, что включает в себя ответ, предоставленный Алексом Холлом.
class StrInt(int):
def __add__(self, val):
return StrInt(super().__add__(int(val)))
__radd__ = __add__
class PropNumberString(object):
def __init__(self, val=None, name='var_name'):
self.val = StrInt(val)
self.name = name
def __get__(self, obj, objtype):
return self.val
def __set__(self, obj, val):
self.val = StrInt(val)
class ConnectionTemplate(object):
tid_field = "ID"
def __init__(self, tid=0):
self.tid = PropNumberString(tid, self.ansa_tid_field)
def __getattribute__(self, key):
# see https://docs.python.org/3/howto/descriptor.html#id5
"Emulate type_getattro() in Objects/typeobject.c"
attrib = object.__getattribute__(self, key)
if hasattr(attrib, '__get__'):
return attrib.__get__(None, self)
return attrib
def __setattr__(self, key, val):
try:
attrib = object.__getattribute__(self, key)
except AttributeError:
self.__dict__[key] = val
return
if hasattr(attrib, '__set__'):
attrib.__set__(None, val)
return
self.__dict__[key] = val
Примечание: Моя мотивация использования пользовательского дескриптора вместо декоратора свойств заключается в том, что я хочу, чтобы мои свойства могли содержать настраиваемые метаданные, то есть значение ключа для дампа json.
Нет хорошего (если вообще есть) способа изменить возвращаемое значение функции в зависимости от контекста, в котором она вызывается.
Вы можете заставить __get__ возвращать объект, тип которого поддерживает сложение int и str, но это намного сложнее, чем просто требовать от пользователя предоставления целого числа, когда вы хотите, чтобы произошло сложение целых чисел.






Вам не нужно определять для этого собственный дескриптор. Вам просто нужно tid вернуть класс, который добавляет так, как вы хотите. Вероятно, лучше сделать преобразование при установке, а не при получении. Вот реализация:
class MyInt(int):
def __add__(self, val):
return MyInt(super().__add__(int(val)))
__radd__ = __add__
class ConnectionTemplate(object):
def __init__(self):
self._tid = 0
@property
def tid(self):
return self._tid
@tid.setter
def tid(self, val):
self._tid = MyInt(val)
Геттер уже возвращает целое число (создается сеттером); проблема в том, что абонент пытается добавить "1", а не 1 к возвращаемому int.
Вы можете, но это не принесет вам никакой пользы;
my_template.tidоценивается какmy_template.tid.__get__(...). Вы пытаетесь добавить 1 к возвращаемому значению__get__, а не к самому дескриптору.