Как реализовать декоратор, подсчитывающий количество вызовов функции?
Объясните на примере простого python-кода. Как мне декорировать функцию, которая подсчитывает количество вызовов функции? При этом, как мне составить правильно и саму инкрементирующую функцию?
def f():
counter = 0
if (f):
counter += 1
return counter
Дополнение к вопросу: Например, разберемся сначала с самой функцией, без декоратора. Почему у меня в результате выполнения функции, выводится "1" ? Ведь функция вызывалась 3 раза.
def f():
counter = 0
if (f):
counter += 1
return counter
f()
f()
f()
result = f()
print(result)
Замыкание – это когда вложенная функция может пользоваться переменными, которые объявлены снаружи этой функции. И, после того, как функция отработает, значение переменной останется в памяти, не будет удалено. И, при каждом вызове функции, когда мы передаем туда каждый раз разный аргумент, отображается каждый раз разное значение.
Декоратор – это функция, которая добавляет новую функциональность к другой функции без изменения её кода. Он как бы оборачивает, декорирует функцию, тем самым расширяя её возможности. Декоратор – это функция, которая принимает другую функцию и возвращает функцию (замыкание).
Ответы (3 шт):
counter - это локальная переменная функции f, она будет обнуляться каждый раз, когда вы вызываете функцию. Если хотите сохранять значение, напишите класс и используйте переменные либо класса, либо экземпляра класса, чтобы сохранить состояние переменных. Здесь я использую экземпляр класса:
class Counter:
def __init__(self):
self.counter = 0
def increment(self):
self.counter += 1
return self.counter
cnt = Counter()
cnt.increment()
cnt.increment()
cnt.increment()
result = cnt.increment()
print(result) # 4
Для счётчика можно использовать и глобальную переменную global counter, так для начинающих обычно проще:
def f():
global counter
counter += 1
return counter
counter = 0
f()
f()
f()
result = f()
print(result) # 4
Но глобальные переменные "замусоривают" память программы и не очень совместимы с принципами ООП, в общем, их лучше не использовать, а поручить конкретные функции конкретному классу, предназначенному для этих функций. Когда вы правильно используете классы, у вас как бы есть гарантия, что класс меняет только своё внутреннее состояние, а когда вы используете глобальные переменные у вас любая функция может поменять любую глобальную переменную и поэтому очень сложно искать и диагностировать ошибки, выяснять, какая функция и в какой момент поменяла состояние программы.
Что касается декораторов - предлагаю вам конкретизировать вопрос, потому что декораторы описывались и разбирались много раз, если вам что-то непонятно в имеющихся объяснениях, добавьте код в вопрос и обозначьте непонятные для вас места кода. Так же как вы сделали с функцией f в вашем вопросе.
Не, ну можно, конечно, и через декоратор:
# Сам декоратор-счётчик
def counter(func):
def wrapper(*args, **kwargs):
wrapper.count += 1
return func(*args, **kwargs)
wrapper.count = 0
return wrapper
# Функция, вызовы которой нужно считать
@counter
def f():
print("Hello")
f()
f()
f()
print(f.count)
Но всё-таки, это извращение. Если у вас есть некое состояние, которое нужно запоминать и потом к нему обращаться, то самый логичный путь - использовать класс. Как именно - уже написали в другом ответе.
Ну и в дополнение ко всему уже изложенному пример класса декортатора.
#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
#
class Decorator:
count = 0
def __init__(self, fun):
self._fun = fun
def __call__(self, *args, **kwargs):
self.count += 1
return self._fun(*args, **kwargs)
@Decorator
def foo(a): ...
foo()
print(foo.count)