Принадлежность объекта к классу после изменения этого объекта

У нас есть простой класс:

class Test:
    n = 5

# Создадим два экземпляра этого класса:
name1 = Test()
name2 = Test()
print(f'{name1.n}, {name2.n}, {Test.n}') # 5, 5, 5

Теперь попробуем изменить один из экземпляров, затем изменим Test.n:

name1.n = 10
print(name1.n) # 10 (Test.n и name2.n на данный момент равны 5)
Test.n = 15 # Изменение самого объекта класса должно повлиять на все ссылки, связанные с ним, но эти изменения не коснуться ссылки name1
print(f'{name1.n}, {name2.n}, {Test.n}') # 10, 15, 15 (соответственно можно предположить, что объект name1 теперь не является объектом класса Test)
#Мы можем проверить принадлежность name1 классу Test:
print(isinstance(name1, Test) # True

Мне объяснили, что этот момент у экземпляра появляется собственный атрибут n, который перекроет (переопределит) родительский, то есть тот, который достался от класса. Но тогда не ясно, почему:

> print(isinstance(name1, Test) # True

Я предполагаю, что на самом деле у нас теперь есть два экземпляра класса Test с одинаковым именем - на первый ссылается name1, на второй ссылается name2. И когда мы пишем Test.n питон работает именно с тем экземпляром на который ссылается name2. Если это так, то где определяется структура экземпляра Test на который ссылается name1? Мы же по сути можем создать в этом экземпляре свои атрибуты, и единственная возможность получить к нему доступ - это через ссылку name1 :

name1.l = 24

В конце концов он может не иметь ничего общего по структуре с классом Test, может для таких объектов без шаблона есть своё название? В общем буду признателен, если просветите, ну либо укажете на то, что я тут несу чушь)


Ответы (1 шт):

Автор решения: Amgarak
class Test:
    n = 5

n - доступен для всех экземпляров, которые его не переопределяли.

name1 = Test()
name2 = Test()
name1.n = 10 

name1.n - тут уже n локальный атрибут.


  • Когда мы изменяем атрибут конкретного экземпляра Test(). То у этого экземпляра переопределяется ссылка на атрибут n. То есть мы перестаем ссылаться на атрибут определённый на уровне класса.
  • Когда изменяется атрибут самого класса Test.n = 15, это влияет на все его экземпляры, которые не переопределили этот атрибут локально. А всё потому, что n продолжает ссылаться на Test.n = 15 даже при создании экземпляра name1 = Test()

При этом, даже если мы переопределим в экземпляре атрибут n, то мы всё равно сможем получить к нему доступ:

class Test:
    n = 5
    def get_n(self):
        print(self.n) # name.n
        print(Test.n)  
        print(self.__class__.n) # name.__class__.n
        print(type(self).n) # type(name).n

name = Test()
name.n = 15
name.get_n()

Вывод:

15
5
5
5

isinstance(name, Test) возвращает True - потому что name всё ещё является экземпляром класса Test и был создан на его основе. Даже если перевести метод isinstance - получим перевод это экземпляр, что собственно намекает?

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

→ Ссылка