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