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 шт):
В 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
В данном конкретном случае можно просто унаследоваться от класса str и добавить нужное поле при инициализации:
class Object(str):
def __new__(cls, value, state):
instance = super().__new__(cls, value)
return instance
def __init__(self, value, state):
self.state = state
Поведение будет ровно такое, как вы хотите.
Но если нужны и другие виды данных в качестве первого аргумента, то придётся ещё подумать.
можно использовать строковое представление обьекта
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
Вы ищете идеальный прокси, т.е. такой который будет доступ (почти) ко всем атрибутам и ко всем специальным методам (типа __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