ЗАМЕР
bench/oop/practice.py
Скрипт, которым получены числа в статье, и запись прогона. Файл читается на сборке из репозитория — это тот самый код, который запускали, а не его копия.
- Цитируется в статье
- /ru/interview/python/classes-and-oop
- Как запустить
python3.11 bench/oop/basics.py python3.12 bench/oop/basics.py python3.13 bench/oop/basics.py python3.14 bench/oop/basics.py
Запись прогона
Замеры для урока «Классы и ООП»
| Скрипт | Что делает |
|---|---|
basics.py |
наблюдения без времени: self и связанный метод, атрибут класса против атрибута экземпляра, три вида методов и фабрики стандартной библиотеки через наследника, super().__init__(), _x и __x, __new__/__init__, __eq__ без __hash__, хеш у датаклассов, __slots__ и словарь экземпляра (раздел 7) |
practice.py |
источник ответов двух практических задач урока |
python3.11 bench/oop/basics.py
python3.12 bench/oop/basics.py
python3.13 bench/oop/basics.py
python3.14 bench/oop/basics.py
Записи в runs/: без суффикса — CPython 3.13.7, остальные версии — с
суффиксом (3.11.15, 3.12.3, 3.14.7); linux/amd64. Перезапущены 14.10.2026 после
добавления раздела 7 — разделы 1–6 не изменились.
Цена вызова метода здесь не меряется: она снята в
bench/attr-lookup/ и разобрана в статье о поиске атрибута. Урок ссылается
туда, а не повторяет числа.
Что получилось
self — обычный первый аргумент. a.hello() и Greeter.hello(a) дали
одно и то же. a.hello — объект типа method, и каждый раз новый:
a.hello is a.hello — False, тысяча обращений — тысяча разных объектов.
Метод, объявленный без self, падает при вызове через экземпляр:
takes 0 positional arguments but 1 was given.
Изменяемый атрибут класса — общий. Три экземпляра добавили по метке в
tags, объявленный в теле класса, — каждый видит все три, а в словаре
экземпляра (vars(c2)) пусто. Присваивание через экземпляр (c1.total = 5) создаёт свой
атрибут и затеняет классовый, класс не меняется.
Три вида методов. @classmethod получает класс, через который вызван:
Dog.create() вернул Dog, а не Animal; так же dict.fromkeys и
datetime.fromtimestamp, вызванные через наследников, вернули наследников.
@staticmethod не получает ничего лишнего.
Забытый super().__init__() — объект создан, но атрибута родителя нет;
AttributeError возникает при первом чтении, а не в конструкторе.
__x переименовывается в _Класс__x. Доступ по исходному имени —
AttributeError, по переименованному — пожалуйста. Наследник с тем же
__pin получает своё имя и родительское не задевает.
__eq__ без __hash__ делает объект нехешируемым: Point.__hash__ —
None, ни элементом множества, ни ключом словаря он быть не может. У
@dataclass хеш по умолчанию тоже None; с frozen=True он генерируется.
Текст исключения в 3.14 изменился и называет роль объекта: cannot use 'Point' as a set element (…) и cannot use 'Point' as a dict key (…) вместо
unhashable type: 'Point' на 3.11–3.13. Остальной вывод на четырёх версиях
совпадает.
__slots__ убирает словарь экземпляра не всегда (раздел 7, добавлен
14.10.2026 по внешнему аудиту, замечание PY-OOP-01). OnlySlots со
__slots__ = ("x",) и базой object — hasattr(o, '__dict__') даёт
False, присвоить o.extra нельзя. Но Child(Base) со __slots__ = (),
где Base — обычный класс, принимает c.extra = 1: vars(c) —
{'extra': 1}. И "__dict__", перечисленный в slots, словарь возвращает.
Текст AttributeError на 3.13 и 3.14 длиннее: … has no attribute 'extra' and no __dict__ for setting new attributes; на 3.11 и 3.12 — без хвоста.
Скрипт
46 строк"""Практика урока «Классы и ООП»: источник ответов двух задач.
Ответ задачи не сочиняется редакцией — он берётся из записи прогона этого
скрипта (`runs/practice.txt`), а сборка сверяет одно с другим
(`scripts/validate-practice.mjs`).
ЧТО ПРОВЕРЯЮТ ЗАДАЧИ. Первая — что изменяемый атрибут, объявленный в теле
класса, один на всех: две корзины пишут в один список. Вторая — что
`obj.method` при каждом обращении даёт НОВЫЙ объект связанного метода:
тысяча обращений, сложенных в список, — тысяча разных объектов.
Запускать: python3.11 / 3.12 / 3.13 / 3.14.
"""
import sys
print("PY", sys.version.split()[0])
print()
print("вывод задачи:")
class Cart:
items = []
def add(self, item):
self.items.append(item)
a = Cart()
b = Cart()
a.add("apple")
b.add("pear")
print(len(a.items), len(b.items), a.items is b.items)
print()
print("оценка:")
class User:
def greet(self):
return "привет"
u = User()
methods = [u.greet for _ in range(1000)]
print("разных объектов связанного метода:", len({id(m) for m in methods}))