Могу ли я определить метод добавления для дескриптора в python 3?

Я сослался на документы 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.

Вы можете, но это не принесет вам никакой пользы; my_template.tid оценивается как my_template.tid.__get__(...). Вы пытаетесь добавить 1 к возвращаемому значению __get__, а не к самому дескриптору.

chepner 22.04.2019 20:43

Нет хорошего (если вообще есть) способа изменить возвращаемое значение функции в зависимости от контекста, в котором она вызывается.

chepner 22.04.2019 20:44

Вы можете заставить __get__ возвращать объект, тип которого поддерживает сложение int и str, но это намного сложнее, чем просто требовать от пользователя предоставления целого числа, когда вы хотите, чтобы произошло сложение целых чисел.

chepner 22.04.2019 20:52
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
3
80
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вам не нужно определять для этого собственный дескриптор. Вам просто нужно 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.

chepner 22.04.2019 20:55

Другие вопросы по теме