Этот вызывается только один раз, когда создаётся дескриптор вызывается впервые (создаётся экземпляр дескриптора x = MyDescriptor()) и в него передаётся имя атрибута (x).
class ValidString:
def __set_name__(self, owner_class, propetry_name):
print(f"owner_class: {owner_class}")
print(f"propetry_name: {propetry_name}")
class Person:
my_new_name = ValidString()
1234567
При запуске этого кода, сразу будет запущен __set_name__(). Сразу на этапе компиляции, даже если не создавать экземпляр класса Person
class ValidString:
def __set_name__(self, owner_class, propetry_name):
self.propetry_name = propetry_name
def __set__(self, instance, value):
if not isinstance(value, str):
raise ValueError(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] = value
def __get__(self, instance, owner):
if instance is None:
return self
#key = "_" + self.propetry_name
#return getattr(instance, key, None)
# аналогичная запись
instance.__dict__.get(self.propetry_name, None)
class Person:
name = ValidString()
surname = ValidString()
p = Person()
123456789101112131415161718192021222324
Примечательно то, что у дескриптора нет метода __init__ - т.к. есть __set_name__ который делает то, что нужно
p.name = "Oleg"
p.__dict__
{"name": "Oleg"}
1234
[!warning] Особенность записи имен в пространство имен с дескрипторами
Значение name запишется сразу в локальное пространство имен экземпляра класса p, а не класса Person - это особенность работы дескриптора.
Если проделать подобное с обычным классом - name запишется в сам класс (если в самом классе существует name, она будет перезаписана):