Подтвердить что ты не робот

Как правильно использовать __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 и аналогичные. Мне нужно "имитировать" это, чтобы имитировать такие вызовы с несколькими точками.

4b9b3361

Ответ 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