Datos de Python y descriptores que no son de datos.

Según la documentación de Python ,

Los descriptores de datos con __set__() y __get__() definidos siempre anulan una redefinición en un diccionario de instancia.

No tengo problemas para entender esta oración, pero ¿puede alguien aclararme por qué existe tal regla? Después de todo, si quiero reemplazar un atributo en un diccionario de instancia, ya necesito hacerlo explícitamente ( inst.__dict__["attr"] = val ), como ingenuo inst.attr = val llamaría el método __set__ del __set__ , lo que (normalmente) no anularía el atributo en el diccionario de instancia.

Edición: solo para dejarlo claro, comprendo lo que está sucediendo, mi pregunta es acerca de por qué se implementó tal regla.

    La anulación se aplica a los descriptores que forman parte de la clase __dict__ .

    Python siempre buscará type(instance).__dict__[attributename].__get__(instance, type(instance)) , y no usará la instance.__dict__ para buscar una anulación de instancia.

    Aquí hay un ejemplo que usa una clase de Descriptor artificial y una propiedad (que es un descriptor con un __get__ y un __set__ :

     >>> class Descriptor(object): ... def __init__(self, name): ... self.name = name ... def __get__(self, instance, cls): ... print 'Getting %s, with instance %r, class %r' % (self.name, instance, cls) ... >>> class Foo(object): ... _spam = 'eggs' ... @property ... def spam(self): ... return self._spam ... @spam.setter ... def spam(self, val): ... self._spam = val ... >>> Foo().spam 'eggs' >>> foo = Foo() >>> foo.__dict__['spam'] = Descriptor('Override') >>> foo.spam 'eggs' 

    Como puede ver, aunque agrego una entrada de spam en la instancia __dict__ , se ignora por completo y la propiedad Foo.spam se usa todavía. Python está ignorando la instancia __dict__ porque la propiedad de spam define tanto __get__ como a __set__ .

    Si usa un descriptor que no define un __set__ el reemplazo funciona (pero __get__ no se llama:

     >>> class Foo(object): ... desc = Descriptor('Class-stored descriptor') ... >>> Foo.desc Getting Class-stored descriptor, with instance None, class  >>> Foo().desc Getting Class-stored descriptor, with instance <__main__.Foo object at 0x1018df510>, class  >>> foo = Foo() >>> foo.__dict__['desc'] = Descriptor('Instance-stored descriptor') >>> foo.desc <__main__.Descriptor object at 0x1018df1d0>