Метод __set_name__

Этот вызывается только один раз, когда создаётся дескриптор вызывается впервые (создаётся экземпляр дескриптора 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, она будет перезаписана):

>>> class Person:
...     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

Чтение имени значения и его запись происходит всегда через дескриптор, поэтому, такой проблемы в дескрипторе нет.

Last updated