Исправьте класс-декоратор Python для методов экземпляра
Напишите решение на Python и проверьте его на странице.
Другие упражнения по этой теме: Классы-декораторы Python: задачи на call, методы и состояние.
Универсальный журнал вызовов
При разработке библиотеки плагинов программист столкнулся с неожиданной ошибкой: если обернуть метод класса обычным декоратором, реализованным как класс, при вызове теряется ссылка на экземпляр. Функция ожидает получить объект в параметре self, но не находит его среди позиционных аргументов и завершает работу сбоем.
Для отладки этой проблемы создаётся универсальный класс-декоратор с протоколом дескриптора __get__. Он проверяет контекст обращения: если функцию вызывают напрямую, она выполняется штатно, а при доступе через экземпляр чужого класса декоратор возвращает связанный метод через types.MethodType, передавая этот экземпляр первым аргументом. При этом декоратор ведёт раздельный счёт вызовов — отдельно для автономных запусков и отдельно для обращений через конкретный объект.
Реализуйте такой декоратор и выясните, сколько вызовов каждого типа было совершено в ходе контрольного прогона.
Что уже дано
В редакторе представлена заготовка класса CallLedger с методами __init__, __call__, __get__ и calls_for.
При выполнении кода доступна вспомогательная функция observe_call_ledger(events), предназначенная для проверки работы дескриптора и счётчиков; реализовывать её самостоятельно не требуется.
Что нужно сделать
Реализуйте класс-декоратор CallLedger(func) со следующими атрибутами и методами:
- Конструктор
__init__(self, func)принимает целевой вызываемый объектfuncи инициализирует атрибутfree_callsцелым числом0. - Прямой вызов экземпляра декоратора
__call__(*args, **kwargs)выполняет целевую функцию с переданными аргументами и увеличивает счётчикfree_callsровно на1. Аргументы*argsи**kwargsперенаправляются без изменений. Если целевая функция завершается ошибкой, счётчикfree_callsвсё равно увеличивается, а возникшее исключение беспрепятственно всплывает дальше. При успешном выполнении возвращается результат целевой функции. - Метод дескриптора
__get__(self, instance, owner=None): - если обращение происходит через класс владельца (
instance is None), метод возвращает сам экземпляр декоратораself; вызов через класс владельца работает по прямому пути с увеличениемfree_calls; - если обращение происходит через конкретный объект (
instanceнеNone), метод возвращает настоящий связанный методtypes.MethodType(self_bound, instance). При вызове такого связанного метода целевой функции первым аргументом передаётсяinstance, за которым следуют переданные позиционные и именованные аргументы, а счётчик вызовов увеличивается строго для этого конкретного экземпляра. - Метод
calls_for(self, instance)возвращает целое число — количество фактических вызовов целевой функции через указанный конкретный экземплярinstance. Для экземпляра, через который ещё не было вызовов, возвращается0. - Простое обращение к атрибуту или сохранение связанного метода в переменную не считается вызовом целевой функции и не меняет счётчики.
- Экземпляры владельца могут быть нехэшируемыми, не иметь собственного словаря
__dict__(использовать__slots__) или переопределять оператор равенства__eq__. Сопоставление экземпляров должно опираться строго на их идентичность (is), а не на равенство или хэши. - Счётчики изолированы: каждый экземпляр
CallLedgerведёт независимый учёт для своей декорированной функции, а вызовы через один объект владельца не влияют на счётчики других объектов.
Ввод и вывод
Стандартные потоки ввода stdin и вывода stdout в решении не используются: считывать ввод и вызывать print() не нужно. После выполнения кода учащегося проверяющая система считывает из пространства имён модуля класс верхнего уровня CallLedger и вызывает его методы в тестовых сценариях.
Пример использования
@CallLedger
def add(a, b):
return a + b
print(add(2, 3))
print(add.free_calls)
class Plugin:
@CallLedger
def repeat(self, text):
return text * 2
plugin = Plugin()
print(plugin.repeat('x'))
print(Plugin.repeat.calls_for(plugin))
Вывод:
5 1 xx 1
Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.
Отправьте решение, чтобы увидеть результаты тестов.
Связанные задачи
- Класс-декоратор Python: когда вызывается init
- Класс-декоратор метода Python: почему теряется self
- Класс-декоратор Python: ограничение изменения результата между вызовами
- Класс-декоратор Python: баланс доступных вызовов функции
- Класс-декоратор Python: изменение аргумента первых N вызовов
- Класс-декоратор Python: буфер событий и пакетная запись
