python: доступ к членам класса как переменной

подскажите можно ли в питоне (и если можно, то как) реализовать следующий функционал:

у класса

class Object:
    def __init__(self, value, state):
        self.value = value
        self.state =  state

c = Object('test', 3.14)

при использовании c будет использоваться value:

print(c) # test
a = "b" + c # btest

при этом можно будет использовать и c.state:

print(c.state) # 3.14
x = c.state - 3 # 0.14

P.S.

и было бы вообще идеально, чтобы до c.value вообще нельзя было бы добраться, как будто его и нет


Ответы (4 шт):

Автор решения: Dmitry

В python есть возможность использование двойного знака подчеркивания, чтобы закрыть доступ по обращению через точку вот так

class Object:
    def __init__(self, value, state):
        self.__value = value
        self.state =  state

Пример

>>> c = Object(1,2) 
>>> c.__value
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Object' object has no attribute '__value'

Но это не говорит, что до этого значения не добраться.

А вот c все равно будет хранить экземпляр класса, если не переопределить стандартный метод __new__, что и реализовано в ответе от @CrazyElf

→ Ссылка
Автор решения: CrazyElf

В данном конкретном случае можно просто унаследоваться от класса str и добавить нужное поле при инициализации:

class Object(str):

    def __new__(cls, value, state):
        instance = super().__new__(cls, value)
        return instance

    def __init__(self, value, state):
        self.state = state

Поведение будет ровно такое, как вы хотите.

Но если нужны и другие виды данных в качестве первого аргумента, то придётся ещё подумать.

→ Ссылка
Автор решения: turkindv

можно использовать строковое представление обьекта

class Object:
    def __init__(self, value, state):
        self.value = value
        self.state =  state

    def __str__(self):
      return self.value



c = Object('test', 3.14)
 
print(c) # test
print("b" + str(c)) #btest

или переопределить __add__, но в таком случае мы должны к обьекту добавлять строку, так же внутри __add__ можно определить разную логику сложения в зависимости от того какого типа обьект мы прибавляем

class Object:
    def __init__(self, value, state):
        self.value = value
        self.state =  state

    def __str__(self):
      return self.value

    def __add__(self, value):
      return self.value + value

c = Object('test', 3.14)

print(c) # test
print(c + "b") #testb

→ Ссылка
Автор решения: Roman-Stop RU aggression in UA

Вы ищете идеальный прокси, т.е. такой который будет доступ (почти) ко всем атрибутам и ко всем специальным методам (типа __repr__, __add__, __iter__ и т.д.) перенаправлять к обернутому объекту.

В общем случае это задача нерешаемая, так как в чем-то объект будет отличаться, например type(c) будет Object, а не type(value), но можно довольно близко к этому подойти.

Вручную в общем случае это делать муторно (хоть и можно), нужно использовать __getattribute__/__getattr__ для обычных атрибутов (в __getattribute__ проверять если идет обращение к дополнительным атрибутам типа state, которых нет в заворачиваемом объекте), а так же вручную прописать перенаправление для всех специальных методов.

Проще использовать ObjectProxy из библиотеки wrapt. Дополнительный атрибут должен иметь префикс _self_, чтоб ObjectProxy доступ к нему не перенаправлял к завернутому объекту:

class CustomProxy(wrapt.ObjectProxy):

    def __init__(self, value, state):
        super(CustomProxy, self).__init__(value)
        self._self_state = state

    @property
    def state(self):
        return self._self_state

    @attribute.setter
    def state(self, value):
        self._self_state = value
→ Ссылка