Перейти к содержанию

232 уроков, 16 библиотек и челлендж «Что выведет код?» — бесплатно, код прямо в браузере

Начать обучение
Урок 3 из 20 Средний 35 мин 110 XP

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

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

Редакция Питоники

Атрибуты из прошлого урока принадлежали конкретному объекту: у каждого игрока своё имя и здоровье. Но у сущностей есть и общее: у всех заказов одна валюта, у всех роботов — версия прошивки, у всех домов — материал по умолчанию. Дублировать общее в каждом объекте расточительно — для этого у класса есть собственные атрибуты.

Атрибут класса: один на всех

Атрибут класса записывается в теле класса, вне методов. Читать его можно и через экземпляр, и через сам класс. А вот записывать через экземпляр опасно: присваивание first.material = "дерево" создаёт атрибут экземпляра, который заслоняет общий, — сам атрибут класса при этом не меняется. Присваивание через экземпляр создаёт атрибут экземпляра и заслоняет атрибут класса, но не меняет его.

заслонение: личное поверх общего
class House:
    material = "кирпич"

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

first = House("ул. Липовая, 7")
second = House("пр. Мира, 1")

first.material = "дерево"

print(first.material)
print(second.material)
print(House.material)
Вывод
дерево
кирпич
кирпич
поиск атрибута: экземпляр, потом класс
class House:
    material = "кирпич"

h = House()
print(h.material)

h.material = "дерево"
print(h.material)

del h.material
print(h.material)
Вывод
кирпич
дерево
кирпич

Поведение объясняется порядком поиска атрибута. Читая h.material, Python смотрит сначала в атрибутах экземпляра, затем в атрибутах класса, затем в родителях класса — о порядке в сложных иерархиях расскажет урок про MRO. Нашлось «дерево» в экземпляре — классовое «кирпич» осталось нетронутым. Удалили del-ом — поиск провалился ниже и снова достал классовое значение. Чтобы сменить значение для всех сразу, присваивайте через класс: House.material = "бетон". Но правило работает лишь для неизменяемых значений — числа и строки переприсваиваются целиком. Списки и словари не переприсваивают, а изменяют на месте — и здесь начинается главная ловушка.

Ловушка изменяемого атрибута класса

классическая ловушка: общий список
class Robot:
    parts = []

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

    def add_part(self, part):
        self.parts.append(part)

roby = Robot("Роби")
wally = Robot("Валли")

roby.add_part("манипулятор")

print(roby.parts)
print(wally.parts)
Вывод
['манипулятор']
['манипулятор']
  1. Метод add_part выполняет self.parts.append(part): Python ищет parts у экземпляра, не находит и берёт общий список класса;
  2. append изменяет список на месте — присваивания нет, а значит, нет и шанса создать личный атрибут;
  3. у Роби и Валли один и тот же список, поэтому деталь «появилась» у обоих — код ошибок не печатал, данные просто общие.
лекарство: изменяемое — в __init__
class Robot:
    def __init__(self, name):
        self.name = name
        self.parts = []

    def add_part(self, part):
        self.parts.append(part)

roby = Robot("Роби")
wally = Robot("Валли")

roby.add_part("манипулятор")

print(roby.parts)
print(wally.parts)
Вывод
['манипулятор']
[]
Что выведет код?

Сначала предскажи ответ в голове — это главный навык программиста.

class Order:
    created = 0

    def __init__(self):
        Order.created += 1

a = Order()
b = Order()
c = Order()
print(Order.created)
class Order:
    created = 0

    def __init__(self):
        self.created += 1

a = Order()
b = Order()
print(a.created, Order.created)
class Team:
    members = []

a = Team()
b = Team()
a.members.append("Анна")
b.members.append("Борис")
print(b.members)
Проверь себя
0 / 6

1. Где создаётся атрибут экземпляра?

2. Что вернёт first.material после first.material = "дерево", если у класса material = "кирпич"?

3. Что произойдёт с House.material после first.material = "дерево"?

4. Почему add_part через self.parts.append(...) меняет список у всех роботов?

5. Как исправить ловушку общего списка?

6. Для чего атрибут класса уместен?

Карточки терминов
Запомнено: 0 / 6
Практика

Создайте класс GameServer с атрибутом класса status = "онлайн" (общий для всех серверов) и __init__(self, region), сохраняющим регион в атрибуте экземпляра. Создайте два сервера — европа и азия, затем переключите статус глобально через класс на «техработы» и выведите две строки: статус азиатского сервера и статус класса.

practice.py
Вопросы и ответы по уроку

Чем атрибут класса отличается от атрибута экземпляра?

Атрибут класса записан в теле класса и один на всех: его видят все экземпляры и сам класс (House.material). Атрибут экземпляра создаётся через self и у каждого объекта свой (self.address). При чтении Python сначала ищет атрибут в экземпляре, затем в классе.

Урок 2: __init__ и self

Почему список, объявленный в теле класса, общий для всех объектов?

Потому что он создаётся один раз при выполнении тела класса, а экземпляры получают не копию, а ссылку на тот же список. Методы вроде append меняют его на месте, и все экземпляры видят изменения. Решение — создавать список в __init__ через self.parts = [].

Как поменять атрибут класса для всех экземпляров?

Через сам класс: House.material = "бетон". Присваивание через экземпляр (first.material = "дерево") создаст личный атрибут экземпляра, который лишь заслонит общий, — класс останется прежним.

Куда Python ищет атрибут, если его нет ни в экземпляре, ни в классе?

Дальше по цепочке родителей: если атрибута нет ни у экземпляра, ни у класса, интерпретатор поднимается по базовым классам. Порядок обхода этой цепочки называется MRO и подробно разбирается в уроке про super() и MRO.

Урок 8: super() и MRO

Понравился урок? Сошлитесь на него

«Присваивание через экземпляр создаёт атрибут экземпляра и заслоняет атрибут класса, но не меняет его.»

Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.

TelegramVK

Похожие уроки по темам

Подобраны автоматически по пересечению тем и ключевых слов.