Обновление переменной класса также влияет на уже созданные объекты

#python #oop

Вопрос:

У меня есть следующий код:

 class ExampleClass:
    varia = 1
    def __init__(self, val, valentered):
        ExampleClass.varia = val
        self.val2=valentered


print(ExampleClass.__dict__)

class_1=ExampleClass(4,"A")

print(ExampleClass.__dict__)

class_2=ExampleClass(3,"B")

print(ExampleClass.__dict__)

print(class_1.varia)
print(class_2.varia)

print(class_1.val2)
print(class_2.val2)
 

Это означает, что у меня есть класс ExampleClass, из которого я создаю объекты class_1 и class_2. Я понимаю, что __init__ каждый раз, когда я создаю объект из класса, я изменяю переменную класса varia. Поэтому я понимаю, что это меняется с 1 на 4, а затем на 3. Однако я ожидал бы, что class_1 все еще имеет объектную переменную со значением 4, а не 3. На выходе получается:

 {'__module__': '__main__', 'varia': 1, '__init__': <function ExampleClass.__init__ at 0x000001A7A02C6EE8>, '__dict__': <attribute '__dict__' of 'ExampleClass' objects>, '__weakref__': <attribute '__weakref__' of 'ExampleClass' objects>, '__doc__': None}
{'__module__': '__main__', 'varia': 4, '__init__': <function ExampleClass.__init__ at 0x000001A7A02C6EE8>, '__dict__': <attribute '__dict__' of 'ExampleClass' objects>, '__weakref__': <attribute '__weakref__' of 'ExampleClass' objects>, '__doc__': None}
{'__module__': '__main__', 'varia': 3, '__init__': <function ExampleClass.__init__ at 0x000001A7A02C6EE8>, '__dict__': <attribute '__dict__' of 'ExampleClass' objects>, '__weakref__': <attribute '__weakref__' of 'ExampleClass' objects>, '__doc__': None}
3
3
A
B
 

Так

 print(class_1.varia)
print(class_2.varia)
 

дает

 3
3
 

Я думал, что когда я создаю объект из класса, этот объект наследует переменные класса (varia) и получает определенные переменные экземпляра (val2), которые могут отличаться для каждого экземпляра, в то время как переменная класса одинакова для всех объектов из этого класса.

Поэтому для меня объект был создан, а затем существует. Он наследуется, но нет обратной связи и, особенно, автоматического обновления. Если я хочу изменить конкретные атрибуты (экземпляр, а также атрибуты объекта) этого объекта, я должен явно изменить этот конкретный объект. Это не помогает мне изменить объект класса в целом, так как объект явно не создается заново. Но я вижу, что ошибаюсь. Когда я запускаю class_2=ExampleClass(3,"B") переменную class_1, она также изменяется на 3 с 4 ранее. Почему это так? Когда рождается ребенок, он рождается один раз. Наследование происходит один раз. Почему тогда изменение родителя должно влиять на ребенка, если я явно не создаю ребенка снова или не обновляю его?

I thought that when I create an object and then another these inherit specific values from a class. But these are two different existing objects. So their object values can differ and do not have to be the same.

Или я должен представить это как один большой пузырь (класс), где существуют пузырьки меньшего размера, которые могут иметь разные атрибуты (экземпляр), но они всегда находятся внутри этого большего пузыря и не могут выйти из него, поэтому их переменные класса одинаковы. Но это означало бы, что не может быть никаких объектов из одного класса с разными объектными переменными/атрибутами (хотя сама переменная класса может быть изменена, но тогда для всех объектов из/в этом классе), и я думаю, что это неправильно.

Чтобы проиллюстрировать мою проблему, можно также уменьшить ее, удалив атрибуты экземпляра:

 class ExampleClass:
    varia = 1
#    def __init__(self, val, valentered):
#        ExampleClass.varia = val
#        self.val2=valentered

class_1=ExampleClass()

ExampleClass.varia=2

class_2=ExampleClass()

print(class_1.varia)
print(class_2.varia)
 

что дает

 2
2
 

и я ожидал

 1
2
 

Комментарии:

1. Правила для этого довольно просты. Когда вы создадите новый экземпляр класса, а он не будет установлен self.var , он получит доступ var из самого класса. Если значение класса var изменится, экземпляр получит последнее значение, т. е. оно изменится. Если вы хотите заморозить значение во время создания экземпляра, просто добавьте в __init__ метод следующее: self.var = self.var . Это позволит получить доступ к значению класса var и сохранить его в экземпляре. С этого момента экземпляр будет иметь свой собственный var , который отделен от значения класса.

2. Поскольку переменная varia является переменной класса, и все объекты этого класса совместно используют эту переменную, необходимо использовать переменную экземпляра.

Ответ №1:

В Python все является объектом.
Экземпляры, естественно, являются объектами. Но классы тоже являются объектами, что на первый взгляд может показаться неочевидным. Разница между классами и экземплярами на самом деле чисто семантическая и условная (имена экземпляров-snake_case, а имена классов — *camelCase), но не техническая.
Вот причина, по которой вы можете вызывать __dict__ экземпляры и классы, потому что оба являются объектами.
Может быть интересно взглянуть на модель данных Python. Класс просто действует как шаблон для создания экземпляров, он является объектом для создания других подобных объектов.

Вы не удивились этому class_1.val2 и class_2.val2 были разными, потому что это два разных объекта, каждый val2 из которых является переменной-членом.
Но есть только один ExampleClass , у которого есть .varia переменная-член. Почему бы этому не измениться ?
Я думаю, вас смутило то, что на самом деле является наследством.

Я думал, что когда я создаю объект из класса, этот объект наследует переменные класса (varia) [ … ], в то время как переменная класса одинакова для всех объектов из этого класса.

Вы не наследуете переменные класса. «Переменная класса» правильнее было бы назвать «обычной переменной-членом экземпляра (но экземпляр является классом)». Без экземпляров это будет работать точно так же :

 class Foo:
    classvar = 0


def just_a_regular_function():
    Foo.classvar  = 1
    print(f"classvar={Foo.classvar}")


just_a_regular_function()  # classvar=1
just_a_regular_function()  # classvar=2
just_a_regular_function()  # classvar=3
just_a_regular_function()  # classvar=4
 

Вы просто изменяете переменную-член экземпляра (который является классом, но это ничего не меняет).

См. Также : Документ Python о классах, раздел о переменных класса