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>