Хмм млин с чего бы начать?
Итак есть классы и есть инстансы(экземпляры классов). Класс это кагбэ шаблон по которому создаеться экземпляр. класс может содержать свои атрибуты а экземпляр свои. Вот банальный пример:
class Test():
a = 'я атрибут "а" класса Test' # это атрибут класса
def __init__(self):
self.b = 'я атрибут "b" экземпляра класса Test' # это атрибут инстанса
if __name__ == '__main__':
print(Test.a)
print(Test.b)
на выходе получаем
я атрибут "а" класса Test
Traceback (most recent call last):
File "<модуль2>", line 7, in <module>
AttributeError: type object 'Test' has no attribute 'b'
мы не создавали никаких экземпляров, но класс(шаблон) уже создан, и мы можем обратиться к атрибуту класса.
Теперь создадим инстанс:
......
if __name__ == '__main__':
test = Test()
print(test.a)
print(test.b)
print('атрибуты инстанса:',test.__dict__)
>>>
>>>
я атрибут "а" класса Test
я атрибут "b" экземпляра класса Test
атрибуты инстанса: {'b': 'я атрибут "b" экземпляра класса Test'}
>>>
>>>
Несмотря на то что сам инстанс не имеет атрибута “а” test.a не бросает исключение.
Когда мы обращаетесь к атрибуту инстанса, сначала атрибут ищеться атрибутах инстанса, если не находит, тогда ищется в атрибутах класса, если и там нету - тогда в атрибутах классов от котороых наследует класс, и если и там не находит то бросает исключение AttributeError.
.....
if __name__ == '__main__':
test = Test()
test.a = 'я атрибут "а" экземпляра класса Test'
print(test.a)
print(test.b)
print('атрибуты инстанса:',test.__dict__)
print(Test.a)
>>>
я атрибут "а" экземпляра класса Test
я атрибут "b" экземпляра класса Test
атрибуты инстанса: {'a': 'я атрибут "а" экземпляра класса Test', 'b': 'я атрибут "b" экземпляра класса Test'}
я атрибут "а" класса Test
>>>
когда мы написали test.a = ‘я атрибут “а” экземпляра класса Test’ то создался атрибут “а” инстанса.
Теперь инстанс класса имеет свой атрибут “а” с своим значением: “я атрибут ”а“ экземпляра класса Test”. В тоже время атрибут класса остался неизменным.
Пока все понятно, это так сказать азы. А теперь добавим дескриптор
class Name(): # name descriptor
def __init__(self):
self.value = 'default name'
def __get__(self, instance, owner):
return self.value
def __set__(self, instance, value):
self.value = value
def __delete__(self, instance):
raise AttributeError('from Name.__delete__')
class Test():
a = 'я атрибут "а" класса Test' # это атрибут класса
c = Name() # это тоже атрибут класса
def __init__(self):
self.b = 'я атрибут "b" экземпляра класса Test' # это атрибут инстанса
if __name__ == '__main__':
test = Test()
print('test.c =', test.c)
test.c = 'John Smith'
print('new name, test.c =',test.c)
print('атрибуты инстанса:',test.__dict__)
test2 = Test()
print('another instance, test.c =',test2.c)
>>>
test.c = default name
new name, test.c = John Smith
атрибуты инстанса: {'b': 'я атрибут "b" экземпляра класса Test'}
another instance, test.c = John Smith
>>>
В предыдущем примере когда мы сделали
“test.a = ‘я атрибут ”а“ экземпляра класса Test’” мы создали новый атрибут инстанса. (“атрибуты инстанса: {'a': ‘я атрибут ”а“ экземпляра класса Test’, ‘b’: ‘я атрибут ”b“ экземпляра класса Test’}”)
Теперь же когда мы делаем
test.c = ‘John Smith’ новый атрибут не созаеться (“атрибуты инстанса: {'b': ‘я атрибут ”b“ экземпляра класса Test’}”) потому что “с” дескриптор, и изменяя атрибут “с” инстанса на самом деле изменяетться атрибут класса Test. Грубо говоря обращаясь дескриптору как к атрибуту инстанса, на самом деле вы обращаетесь к атрибуту класса. Безоговорочно.
Соответвенно если вы создадите другой инстанст класса Test и захотите изменить атрибут “с” он опять изменить атрибут класса а не инстанса.