Сообщить об ошибке.

Атрибуты класса и переменные экземпляра класса в Python

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

Python вызывает специальный метод __init__(), который называют конструктором класса, каждый раз при создании нового экземпляра класса.

class Dog:

    # атрибут данных (переменная класса), 
    # общая для всех экземпляров класса
    kind = 'canine'

    def __init__(self, name):
        # переменная экземпляра класса
        # уникальна для каждого экземпляра
        self.name = name

>>> d = Dog('Fido')
>>> e = Dog('Buddy')

# переменная `kind` будет общая для 
# всех экземпляров объекта `Dog`
>>> d.kind
# 'canine'
>>> e.kind    
# 'canine'

# переменная `name` будет уникальна 
# для каждого из экземпляров
>>> d.name
# 'Fido'
>>> e.name    
# 'Buddy'

Как говорилось в материале "Классы в языке Python", общие данные могут иметь неожиданный эффект при использовании изменяемых объектов, таких как списки и словари. Например, список tricks (трюки, которые может делать отдельная собака) в примере ниже не следует использовать как атрибут данных/переменную класса, потому что для всех экземпляров класса Dog будет использоваться только один атрибут данных tricks:

class Dog:

    # ошибочное использование атрибута tricks -
    #  переменной класса
    tricks = []

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

    def add_trick(self, trick):
        self.tricks.append(trick)

>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.add_trick('roll over')
>>> e.add_trick('play dead')

# неожиданно разделяется всеми собаками
>>> d.tricks
# ['roll over', 'play dead']

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

class Dog:
    def __init__(self, name):
        self.name = name
        # создает новый пустой список 
        # трюков для каждой собаки
        self.tricks = []

    def add_trick(self, trick):
        self.tricks.append(trick)

>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.add_trick('roll over')
>>> e.add_trick('play dead')
>>> d.tricks
# ['roll over']
>>> e.tricks
# ['play dead']

Если одно и то же имя атрибута встречается как в экземпляре класса, так и в самом классе, то поиск атрибута определяет приоритет экземпляра класса:

>>> class Warehouse:
        purpose = 'storage'
        region = 'west'

>>> w1 = Warehouse()
>>> print(w1.purpose, w1.region)
# storage west
>>> w2 = Warehouse()
>>> w2.region = 'east'
>>> print(w2.purpose, w2.region)
# storage east

На атрибуты данных класса могут ссылаться как методы, так и обычные пользователи - "клиенты" объекта. Другими словами, классы не могут использоваться для реализации чисто абстрактных типов данных. Фактически, ничто в Python не позволяет принудительно скрывать данные - все основано на соглашении.

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

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

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

  • getattr(obj, name [, default]) - для доступа к атрибуту name объекта класса obj.
  • hasattr(obj, name) - проверить, есть ли в классе obj атрибут name.
  • setattr(obj, name, value) - задать атрибут name со значением value. Если атрибут не существует, он будет создан.
  • delattr(obj, name) - удалить атрибут name из объекта класса obj.

Встроенные атрибуты класса.

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

  • __dict__ - словарь, содержащий пространство имен класса.
  • __doc__ - строка документации класса. None если, документация отсутствует.
  • __name__ - имя класса.
  • __module__ - имя модуля, в котором определяется класс.
  • __bases__ - кортеж, содержащий базовые классы, в порядке их появления. Кортеж будет пустым, если наследование не было.
  • __mro__ - Порядок разрешения методов в множественном наследовании.

Где хранятся атрибуты класса и экземпляра класса?

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

class MyClass:
    class_attr = "Class"

    def __init__(self):
        self.instance_attr = "Instance"

>>> my_object = MyClass()
# атрибут экземпляра класса
>>> my_object.__dict__
# {'instance_attribute': 'Instance'}

# атрибут экземпляра класса
>>> MyClass.__dict__['class_attr']
# 'Class'

>>> my_object.class_attr
'Class'
>>> my_object.instance_attr
'Instance'