Этот вызывается только один раз, когда создаётся дескриптор вызывается впервые (создаётся экземпляр дескриптора x = MyDescriptor()) и в него передаётся имя атрибута (x).
При запуске этого кода, сразу будет запущен __set_name__(). Сразу на этапе компиляции, даже если не создавать экземпляр класса Person
classValidString:def__set_name__(self,owner_class,propetry_name): self.propetry_name = propetry_namedef__set__(self,instance,value):ifnotisinstance(value, str):raiseValueError(f"{self.propetry_name} must be a String, but {type(value)} was passed")#key = "_" + self.propetry_name#setattr(instance, key, value)# аналогичная запись instance.__dict__[self.propetry_name] = valuedef__get__(self,instance,owner):if instance isNone:return self#key = "_" + self.propetry_name#return getattr(instance, key, None)# аналогичная запись instance.__dict__.get(self.propetry_name, None)classPerson: name =ValidString() surname =ValidString()p =Person()123456789101112131415161718192021222324
Примечательно то, что у дескриптора нет метода __init__ - т.к. есть __set_name__ который делает то, что нужно
p.name ="Oleg"p.__dict__{"name":"Oleg"}1234
[!warning] Особенность записи имен в пространство имен с дескрипторами
Значение name запишется сразу в локальное пространство имен экземпляра класса p, а не класса Person - это особенность работы дескриптора.
Если проделать подобное с обычным классом - name запишется в сам класс (если в самом классе существует name, она будет перезаписана):
>>>classPerson:... name ="Ivan"...>>> p =Person()>>> p.name'Ivan'>>> p.__dict__{}>>> p.name ="Dima">>> p.name'Dima'>>> p.__dict__{'name':'Dima'}>>> Person.name'Ivan'>>> Person.__dict__mappingproxy({'__module__': '__main__', 'name': 'Ivan', '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None})
1234567891011121314151617
Чтение имени значения и его запись происходит всегда через дескриптор, поэтому, такой проблемы в дескрипторе нет.