web-dev-qa-db-fra.com

Comment utiliser __setattr__ correctement, en évitant la récursion infinie

Je veux définir une classe contenant la méthode read et write, qui peut être appelée comme suit:

instance.read
instance.write
instance.device.read
instance.device.write

Pour ne pas utiliser de classes entrelacées, mon idée était d'écraser le __getattr__ et __setattr__ méthodes et pour vérifier si le nom donné est device pour rediriger le retour vers self. Mais j'ai rencontré un problème donnant des récursions infinies. L'exemple de code est le suivant:

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)

Un péché __init__ le code a essayé de créer un nouvel attribut x, il appelle __setattr__, qui appelle à nouveau __setattr__ etc. Comment dois-je changer ce code, que, dans ce cas, un nouvel attribut x de self est créé, contenant la valeur 1?

Ou existe-t-il une meilleure façon de gérer les appels comme instance.device.read à "mapper" à instance.read?

Comme il y a toujours des questions sur le pourquoi: j'ai besoin de créer des abstractions d'appels xmlrpc, pour lesquelles des méthodes très simples comme myxmlrpc.instance,device.read et similaires peuvent être créés. J'ai besoin de "simuler" cela pour imiter de tels appels à plusieurs points.

47
Alex

Vous devez appeler la classe parent __setattr__ méthode:

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)

En ce qui concerne la meilleure pratique, puisque vous prévoyez de l'utiliser via xml-rpc Je pense que c'est probablement mieux de le faire à l'intérieur du _dispatch méthode.

Un moyen rapide et sale est de simplement faire:

class My(object):
    def __init__(self):
        self.device = self
49
Bakuriu

Ou vous pouvez modifier self.__dict__ De l'intérieur __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
18
Vindolin

Vous pouvez également utiliser objet.

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
Danny Harpigny

ou vous pouvez simplement utiliser @property:

class MyTest(object):

    def __init__(self, x):
        self.x = x

    @property
    def device(self):
        return self
0
Aviad Rozenhek