Как правильно использовать __setattr__, избегая бесконечной рекурсии
Я хочу определить класс, содержащий read
и write
метод, который можно вызвать следующим образом:
instance.read
instance.write
instance.device.read
instance.device.write
Чтобы не использовать чересстрочные классы, моя идея заключалась в том, чтобы перезаписать методы __getattr__
и __setattr__
и проверить, если данное имя device
перенаправляет возврат на self
. Но я столкнулся с проблемой, дающей бесконечные рекурсии. Код примера выглядит следующим образом:
class MyTest(object):
def __init__(self, x):
self.x = x
def __setattr__(self, name, value):
if name=="device":
print "device test"
else:
setattr(self, name, value)
test = MyTest(1)
Как и в __init__
, код попытался создать новый атрибут x
, он вызывает __setattr__
, который снова вызывает __setattr__
и так далее. Как мне изменить этот код, в этом случае создается новый атрибут x
из self
, удерживая значение 1
?
Или есть ли лучший способ обрабатывать вызовы, такие как instance.device.read
, для "сопоставления" с instance.read
?
Как всегда есть вопросы о том, почему: мне нужно создать абстракции вызовов xmlrpc
, для которых могут быть созданы очень простые методы вроде myxmlrpc.instance,device.read
и аналогичные. Мне нужно "имитировать" это, чтобы имитировать такие вызовы с несколькими точками.
Ответы
Ответ 1
Вы должны вызвать метод родительского класса __setattr__
:
class MyTest(object):
def __init__(self, x):
self.x = x
def __setattr__(self, name, value):
if name=="device":
print "device test"
else:
super(MyTest, self).__setattr__(name, value)
# in python3+ you can omit the arguments to super:
#super().__setattr__(name, value)
Что касается лучшей практики, так как вы планируете использовать это через xml-rpc
, я думаю, что это, вероятно, лучше сделать внутри _dispatch
метод.
Быстрый и грязный способ - просто:
class My(object):
def __init__(self):
self.device = self
Ответ 2
Или вы можете изменить self.__dict__
изнутри __setattr__()
:
class SomeClass(object):
def __setattr__(self, name, value):
print(name, value)
self.__dict__[name] = value
def __init__(self, attr1, attr2):
self.attr1 = attr1
self.attr2 = attr2
sc = SomeClass(attr1=1, attr2=2)
sc.attr1 = 3
Ответ 3
Вы также можете использовать объект.
class TestClass:
def __init__(self):
self.data = 'data'
def __setattr__(self, name, value):
print("Attempt to edit the attribute %s" %(name))
object.__setattr__(self, name, value)
Ответ 4
или вы можете просто использовать @property:
class MyTest(object):
def __init__(self, x):
self.x = x
@property
def device(self):
return self