I'm trying to understand descriptors and wrote a simple example that practically emulates the given one:
class Descriptor:
def __set_name__(self,obj,name):
self.name=name
def __set__(self,obj,valor):
if (self.name).title()=='Nombre':
if type(valor)!=str:
raise TypeError('No es un string')
else:
print(f'Estamos cambiando el atributo nombre de {obj} a {valor}')
setattr(obj,self.name,valor)
else: print('Hola')
def __get__(self,obj,owner):
return getattr(obj,self.name)
class Prueba:
nombre=Descriptor()
apellido=Descriptor()
print(nombre.__dict__)
print(apellido.__dict__)
def __init__(self,nombre,apellido):
self.nombre=nombre
self.apellido=apellido
baz=Prueba('foo','bar')
print(baz.__dict__)
Which makes the Jupyter Kernel crash (had never happened before).
If I change every nombre or apellido, I get:
{}
{}
Hola
Hola
{}
So somehow the instances of the Descriptor class are empty. Yet when I make Descriptor print self.name in **set **it works. I'm very confused about how to use them to simplify properties.
The problem here is an infinite regress in the
__get__and__set__methods of the descriptor. For the sake of example, let's focus just on the descriptor object for thenombreattribute, which hasself.name == 'nombre'.When you initialise an instance of
Prueba:In
__init__, executingself.nombre = nombreinvokes the descriptor's__set__method (as expected)That
__set__method invokessetattr(obj, 'nombre', valor)(because the descriptor'sself.nameis'nombre')The value of object's
nombreattribute is the descriptor itself. So thatsetattrcall invokes__set__in the descriptor again.So steps 2 and 3 repeat cyclically until the recursion depth is exceeded.
Similarly, a call to
__get__executesgetattr(obj, 'nombre'). Butobj.nombrewhich is the descriptor object itself, so the result is another call to the descriptor's__get__, and so on in an infinite cycle.The docs show a way that you can programmatically store attribute values using a "private" name that avoids this regress.
So your example descriptor becomes:
With this in place, your example of:
now gives:
and
print(baz.nombre)printsfoo.