DevGang
Авторизоваться

Понимание переменных класса и экземпляра в Python 3 

При изучении объектно-ориентированного программирования на Python может возникнуть несколько сложностей, когда дело доходит до разграничения переменных класса и экземпляра. В этом руководстве я объясню разницу между переменными класса и экземпляра и приведу примеры, демонстрирующие различные варианты использования.

Переменные класса и экземпляра

Во-первых, быстрый обзор, если вы новичок в объектно-ориентированном программировании. Класс - это шаблон для создания объектов, а экземпляр - это сам объект. Классы часто представляют что-то в реальном мире, так что представьте, хотите ли вы создать класс, списка студентов. Вы можете создать класс с именем Student, который представляет собой шаблон, который определяет различные атрибуты студента. Таким образом, каждый студент является экземпляром класса Student.

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

Переменные класса

Переменные класса обычно являются переменными, которые являются общими для всех экземпляров. И они определены так:

class Student:
   teacher = 'Mrs. Jones'  # переменная класса

Каждый экземпляр класса будет иметь одинаковое значение для этих переменных:

tom = Student()
susan = Student()

print(tom.teacher)
>> "Mrs. Jones"

print(susan.teacher)
>> "Mrs. Jones"

Переменные экземпляра

Переменные экземпляра (также называемые атрибутами данных) уникальны для каждого экземпляра класса и определяются в методе класса, например:

class Student:
   teacher = 'Mrs. Jones'  # переменная класса   

   def __init__(self, name):
       self.name = name  # переменная экземпляра

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

tom = Student('Tom')
susan = Student('Susan')

print(tom.name)
>> "Tom"

print(susan.name)
>> "Susan"

Резюме

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

Чего ожидать от переменных класса

Переменные класса являются общими для всех экземпляров класса. Напоминаем, что они определены так:

class Student:
   teacher = 'Mrs. Jones'

Иными словами, переменные класса ссылаются на одно и то же место в памяти. Смотрите следующее:

tom = Student()
susan = Student()

id(tom.teacher) == id(susan.teacher)
>> True

Функция id возвращает адрес объекта в памяти для реализации CPython.

Таким образом, с помощью функции id мы можем подтвердить, что атрибут teacher ссылается на то же место в памяти.

Изменение переменной класса

Что произойдет, если мы изменим переменную класса даже после создания экземпляров?

tom = Student()

tom.teacher
>> Mrs. Jones

Student.teacher = 'Mr. Smith'

tom.teacher
>> Mr. Smith

Как и следовало ожидать, поскольку переменная teacher ссылается на общее местоположение в памяти, она также обновляется в экземпляре.

Изменение переменной экземпляра

Это, вероятно, наиболее очевидное и ожидаемое поведение, поэтому не стесняйтесь пропустить этот шаг. Но я все же покажу несколько примеров для полноты.

Рассмотрим наш класс Student с переменными класса и экземпляра:

class Student:
   teacher = 'Mrs. Jones'

   def __init__(self, name):
       self.name = name

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

tom = Student('Tom')
susan = Student('Susan')

id(tom.name) == id(susan.name)
>> False

Как и следовало ожидать, обновление атрибута name в одном экземпляре не влияет на другой:

tom.name
>> Tom

susan.name
>> Susan

tom.name = 'Thomas'
tom.name
>> Thomas

susan.name
>> Susan

Переменные экземпляра переопределяют переменные класса (и методы)

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

Помните следующее?

tom = Student()
susan = Student()

id(tom.teacher) == id(susan.teacher)
>> True

Что произойдет, если мы изменим атрибут teacher прямо в одном из случаев:

tom.teacher = 'Mr. Clark'

Это важно отметить: переменные экземпляров не нужно объявлять, они создаются всякий раз, когда им присваиваются, а переменные экземпляра переопределяют переменные класса. Это означает, что в экземпляре Tom teacher больше не ссылается на переменную класса, а на вновь созданную переменную экземпляра.

И, естественно, экземпляр Сьюзен не затронут:

tom.teacher
>> Mr. Clark

susan.teacher
>> Mrs. Jones

Надеюсь, вы видите, как такое поведение может привести к путанице. По этой причине важно сохранять организованные имена переменных. Если переменная объявлена как переменная класса, она (обычно) не должна быть переопределена. Переменные экземпляра могут быть определены в очевидных местах, например, метод __init__. Часто хорошо придумать соглашение об именовании переменных. Например, методы класса должны быть глаголами, существительными переменных класса и существительными переменных экземпляра с префиксом «_».

Использование изменяемых объектов в качестве переменных класса

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

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

class Student:
   teacher = 'Mrs. Jones'
   test_scores = []

   def __init__(self, name):
       self.name = name

   def add_score(self, score):
       self.test_scores.append(score)

У нас есть переменная класса для хранения баллов, и у нас есть метод класса для добавления баллов. Теперь давайте добавим несколько баллов.

tom = Student('Tom')
susan = Student('Susan')

tom.add_score(90)
susan.add_score(100)

Можете ли вы угадать, какую ошибку мы только что сделали?

tom.test_scores
>> [90, 100]

Да, test_scores - это переменная класса, а не переменная экземпляра. Каждый экземпляр просто добавляет значения в переменную класса. Мы действительно хотим, чтобы каждый экземпляр содержал свой собственный список test_scores.

Так что лучший класс может выглядеть так:

class Student:
   teacher = 'Mrs. Jones'

   def __init__(self, name):
       self.name = name
       self.test_scores = []

   def add_score(self, score):
       self.test_scores.append(score)

И теперь наша проблема решена!

Заключение

Надеюсь, вы узнали разницу между переменными класса и экземпляра и что ожидать от каждой из них. Если я что-то упустил в этом руководстве или у вас есть отличные примеры путаницы переменных класса и экземпляра, пожалуйста, прокомментируйте ниже. Я был бы рад добавить их в это руководство!

#Начинающим #Python
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу