Метод __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__ который делает то, что нужно

[!warning] Особенность записи имен в пространство имен с дескрипторами Значение name запишется сразу в локальное пространство имен экземпляра класса p, а не класса Person - это особенность работы дескриптора.

Если проделать подобное с обычным классом - name запишется в сам класс (если в самом классе существует name, она будет перезаписана):

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

Last updated

Was this helpful?