Декоратор для рекурсии
У меня есть декоратор, подсчитывающий количество вызовов рекурсивной функции ( ncalls ) и глубину рекурсии ( rdepth ) Для не рекурсивной функции значения будут (1,1) На примерах, которые придумала я, и на примерах из контекста работает правильно, но при сдаче в контексте не проходит один тест, но я никак не могу придумать ситуацию, которая бы сломала мой код. counter.flag и counter.w (не зануляется только когда вышли из функции столько же раз, как глубоко заходили) нужны чтобы понимать, когда мы в данную функцию заходим первый раз и когда нужно сбросить счетчики, counter.d считает глубину рекурсии в текущий момент
def counter(func): counter.flag = True counter.w = 0 counter.d = 0 @functools.wraps(func) def count_f(*args, **kwargs): if counter.flag: count_f.ncalls = 0 count_f.rdepth = 0 counter.flag = False counter.w = 0 counter.d = 0 count_f.ncalls += 1 counter.d += 1 counter.w = 0 if count_f.rdepth < counter.d: count_f.rdepth = counter.d result = func(*args, **kwargs) counter.d -= 1 counter.w += 1 if counter.w == count_f.rdepth: counter.flag = True return result return count_f
@counter def fib(n): if n == 0 or n == 1: return n else: return fib(n - 2) + fib(n - 1) fib(3) print(fib.ncalls, fib.rdepth) # 5 3
@counter def func2(n, steps): if steps == 0: return func2(n + 1, steps - 1) func2(n - 1, steps - 1) if __name__ == "__main__": func2(0, 5) print(func2.ncalls, func2.rdepth) func2(0, 3) print(func2.ncalls, func2.rdepth) # 63 6 # 15 4
Как подсчитать общее количество вызовов метода класса в Python?
Представьте, что в игре есть класс “Spaceship”, в котором у игроков есть возможность, среди прочего, изменить название корабля после его создания:
class Spaceship: def __init__(self, name, capitan): self.name = name self.capitan = capitan def print_data(self): data = < 'name': self.name, 'capitan': self.capitan >print data def rename(self, name): self.name = name ship = Spaceship('Звезда смерти', 'Цеситар') ship.rename('Тысячелетний сокол') ship.print_data() # Результат
Если бы я хотел узнать, сколько раз менялось название корабля, я мог бы добавить счетчик:
class Spaceship: def __init__(self, name, capitan): self.name = name self.capitan = capitan self.total_renames = 0 def print_data(self): data = < 'name': self.name, 'capitan': self.capitan, 'total_renames': self.total_renames >print data def rename(self, name): self.name = name self.total_renames += 1 ship = Spaceship('Звезда смерти', 'Цеситар') ship.print_data() ship.rename('Тысячелетний сокол') ship.print_data() # Результат
Но это поможет мне узнать, сколько раз каждый экземпляр класса, т.е. каждый корабль, был переименован:
Code language: PHP (php)ship = Spaceship('Звезда смерти', 'Цеситар') ship.print_data() ship.rename('Тысячелетний сокол') ship.print_data() ship = Spaceship('USS Enterprise', 'Фьерелла') ship.print_data() ship.rename('Центавра') ship.print_data() ship.rename('Челенджер') ship.print_data() # Результат 'name': 'Звезда смерти', 'capitan': 'Цеситар', 'total_renames': 0> 'name': 'Тысячелетний сокол', 'capitan': 'Цеситар', 'total_renames': 1> 'name': 'Энтерпрайз', 'capitan': 'Фьерелла', 'total_renames': 0> 'name': 'Центавра', 'capitan': 'Фьерелла', 'total_renames': 1> 'name': 'Челенджер', 'capitan': 'Фьерелла', 'total_renames': 2>
Мне нужно подсчитать общее количество вызовов метода класса, а не общее количество вызовов для каждого экземпляра. Я знаю, что одним из способов может быть накопление всех кораблей и сложение атрибута total_renames каждого из них:
ships = [ship1, ship2, ship3] total = sum([ship.total_renames for ship in ships])
Но я хочу знать, можно ли сделать это на уровне класса, как некую постоянную переменную на время жизни игры. Есть ли способ сделать это?
Примечание: на данный момент я хочу сохранить простоту и не использовать базы данных.
Ответ.
Лучший способ реализовать это, на мой взгляд, это декоратор, который обрабатывает статическую переменную ( добавляю комментарии ко всем методам):
# -*- coding: utf-8 -*- class Spaceship: total_renames = 0 def __init__(self, name, capitan): self.name = name self.capitan = capitan def print_data(self): data = < 'name': self.name, 'capitan': self.capitan, 'total_renames': Spaceship.total_renames >print data def _dec_increases_renames(func): """ Это функция-декоратор, которая отвечает за вызов метода класса _increases_counter_renames. Инкремент не работает, если делать его внутри функции "internal". Последний должен вызывать метод класса. """ def internal(cls, *args, **kwargs): result = func(cls, *args, **kwargs) cls._increases_counter_renames() return result return internal @_dec_increases_renames def rename(self, name): """Метод декорирован с увеличением счетчика.""" self.name = name @classmethod def _increases_counter_renames(cls): """ Это метод класса, который только увеличивает статический счетчик. """ cls.total_renames += 1 ship = Spaceship('Звезда смерти', 'Цесистар') ship.print_data() ship.rename('Тысячелетний сокол') ship.print_data() ship = Spaceship('Энтерпрайз', 'Фьерелла') ship.print_data() ship.rename('Центавра') ship.print_data() ship.rename('Челенджер') ship.print_data()
Вывод этого кода соответствует требованиям:
Code language: JavaScript (javascript)'name': 'Звезда смерти', 'capitan': 'Цесистар', 'total_renames': 0> 'name': 'Тысячелетний сокол', 'capitan': 'Цесистар', 'total_renames': 1> 'name': 'Энтерпрайз', 'capitan': 'Фьерелла', 'total_renames': 1> 'name': 'Центавра', 'capitan': 'Фьерелла', 'total_renames': 2> 'name': 'Челенджер', 'capitan': 'Фьерелла', 'total_renames': 3>
Поэтому рекомендуются следующие шаги:
- Добавьте статическую переменную с начальным значением 0.
- Реализуйте метод класса, который при вызове только увеличивает статическую переменную с предыдущего значения.
- Реализуйте метод-декоратор, внутренняя функция которого просто вызывает метод класса из предыдущего пункта.
- Увеличение счетчика в этой внутренней функции не работает.
- Используйте вышеупомянутый декоратор для декорирования функции, вызовы которой мы хотим подсчитать.
Как подсчитать количество вызова функции Python
Как подсчитать количество вызова функции и как вывести. Как это сделать с помощью декоратора и как с помощью логирования, если это возможно и какие есть еще способы. Важно: Без внешнего кода и глобальных переменных.
3 ответа 3
Вот таким декоратором делаем иньекцию переменной счётчика в функцию:
def counter(fu): def inner(*a,**kw): inner.count+=1 return fu(*a,**kw) inner.count = 0 return inner @counter def test(): pass @counter def test1(): print(test1.count) test() test() test1() test1() test1() print(test.count) print(test1.count)
Внутри функции этот счётчик доступен.
def count(func): """ Декоратор - счётчик """ counters = <> def wrapper(*args, **kwargs): counters[func] = counters.get(func, 0) + 1 print(f'Функция вызвана раз') return func(*args, **kwargs) return wrapper @count def double_function(a): """ Умножаем полученный параметр. """ return a*2 @count def triple_function(a): """ Утраиваем """ return a*3 if __name__ == "__main__": print(list(map(double_function, range(2)))) print(list(map(triple_function, range(4)))) print(list(map(double_function, range(10,11)))) print(list(map(triple_function, range(12,13))))
Функция double_function вызвана 1 раз Функция double_function вызвана 2 раз [0, 2] Функция triple_function вызвана 1 раз Функция triple_function вызвана 2 раз Функция triple_function вызвана 3 раз Функция triple_function вызвана 4 раз [0, 3, 6, 9] Функция double_function вызвана 3 раз [20] Функция triple_function вызвана 5 раз [36]
Декораторы
Декораторы в Python и примеры их практического использования.
Итак, что же это такое? Для того, чтобы понять, как работают декораторы, в первую очередь следует вспомнить, что функции в python являются объектами, соответственно, их можно возвращать из другой функции или передавать в качестве аргумента. Также следует помнить, что функция в python может быть определена и внутри другой функции.
Вспомнив это, можно смело переходить к декораторам. Декораторы — это, по сути, "обёртки", которые дают нам возможность изменить поведение функции, не изменяя её код.
Создадим свой декоратор "вручную":
Наверное, теперь мы бы хотели, чтобы каждый раз, во время вызова stand_alone_function, вместо неё вызывалась stand_alone_function_decorated. Для этого просто перезапишем stand_alone_function:
Собственно, это и есть декораторы. Вот так можно было записать предыдущий пример, используя синтаксис декораторов:
То есть, декораторы в python — это просто синтаксический сахар для конструкций вида:
При этом, естественно, можно использовать несколько декораторов для одной функции, например так:
Однако, все декораторы, которые мы рассматривали, не имели одного очень важного функционала — передачи аргументов декорируемой функции. Собственно, это тоже несложно сделать.
Декорирование методов
Один из важных фактов, которые следует понимать, заключается в том, что функции и методы в Python — это практически одно и то же, за исключением того, что методы всегда ожидают первым параметром ссылку на сам объект (self). Это значит, что мы можем создавать декораторы для методов точно так же, как и для функций, просто не забывая про self.
Конечно, если мы создаём максимально общий декоратор и хотим, чтобы его можно было применить к любой функции или методу, то можно воспользоваться распаковкой аргументов:
Python is cool, no argument here. 1 2 3 Любят ли Билл, Линус и Стив утконосов? Определенно! ,) <> Мне 28 лет, а ты бы сколько дал?
Декораторы с аргументами
А теперь попробуем написать декоратор, принимающий аргументы:
Теперь перепишем данный код с помощью декораторов:
Вернёмся к аргументам декораторов, ведь, если мы используем функцию, чтобы создавать декораторы "на лету", мы можем передавать ей любые аргументы, верно?
Таким образом, мы можем передавать декоратору любые аргументы, как обычной функции. Мы можем использовать и распаковку через *args и **kwargs в случае необходимости.
Некоторые особенности работы с декораторами
- Декораторы несколько замедляют вызов функции, не забывайте об этом.
- Вы не можете "раздекорировать" функцию. Безусловно, существуют трюки, позволяющие создать декоратор, который можно отсоединить от функции, но это плохая практика. Правильнее будет запомнить, что если функция декорирована — это не отменить.
- Декораторы оборачивают функции, что может затруднить отладку.
Последняя проблема частично решена добавлением в модуле functools функции functools.wraps, копирующей всю информацию об оборачиваемой функции (её имя, из какого она модуля, её документацию и т.п.) в функцию-обёртку.
Забавным фактом является то, что functools.wraps тоже является декоратором.
Декораторы могут быть использованы для расширения возможностей функций из сторонних библиотек (код которых мы не можем изменять), или для упрощения отладки (мы не хотим изменять код, который ещё не устоялся).
Также полезно использовать декораторы для расширения различных функций одним и тем же кодом, без повторного его переписывания каждый раз, например:
wrapper 0.00011799999999997923 арозА упал ан алапу азор А wrapper 0.00017800000000001148 !amanaP :lanac a ,noep a ,stah eros ,raj a,hsac ,oloR a ,tur a ,mapS ,snip ,eperc a , .
Для вставки кода на Python в комментарий заключайте его в теги