Счетчик вызовов функции python

Счетчик функций подсчета функций Python

Я обновляю свою память о некоторых функциях python, которых я еще не получил, я изучаю этот учебник по python и вот пример, который я не совсем понимаю. Это о декораторе, который подсчитывает вызовы функции, вот код:

def call_counter(func): def helper(x): helper.calls += 1 return func(x) helper.calls = 0 return helper @call_counter def succ(x): return x + 1 if __name__ == '__main__': print(succ.calls) for i in range(10): print(succ(i)) print(succ.calls) 

Я не понимаю, почему мы увеличиваем вызовы оболочки функций (helper.calls + = 1) вместо вызовов функций и почему она действительно работает?

Важно помнить о декораторах, что декоратор – это функция, которая принимает функцию как аргумент и возвращает еще одну функцию. Возвращаемое значение – еще одна функция – это то, что будет вызываться при вызове имени исходной функции.

Эта модель может быть очень простой:

def my_decorator(fn): print("Decorator was called") return fn 

В этом случае возвращаемая функция такая же, как и переданная функция. Но это обычно не то, что вы делаете. Обычно вы возвращаете либо совершенно другую функцию, либо возвращаете функцию, которая каким-то образом целяет или обертывает исходную функцию.

В вашем примере, который является очень распространенной моделью, вы возвращаете внутреннюю функцию:

def helper(x): helper.calls += 1 return func(x) 

Эта внутренняя функция вызывает исходную функцию ( return func(x) ), но она также увеличивает счетчик вызовов.

Эта внутренняя функция вставляется как “замена” для любой функции, которая оформляется. Поэтому, когда foo.succ() модуля foo.succ() просматривается, результат является ссылкой на внутреннюю вспомогательную функцию, возвращаемую декоратором. Эта функция увеличивает счетчик вызовов, а затем вызывает первоначально определенную функцию succ .

Я не понимаю, почему мы увеличиваем вызовы оболочки функций (helper.calls + = 1) вместо вызовов функций и почему она действительно работает?

Я считаю, что это универсальный декоратор. Вы можете это сделать

def succ(x): succ.calls += 1 return x + 1 if __name__ == '__main__': succ.calls = 0 print(succ.calls) for i in range(10): print(succ(i)) print(succ.calls) 

который работает очень хорошо, но вам нужно будет поместить .calls +=1 в каждую функцию, которую вы хотели применить, и инициализировать до 0, прежде чем вы запустите какой-либо из них. Если бы у вас была целая куча функций, которые вы хотели считать, это определенно лучше. Плюс он инициализирует их 0 при определении, что приятно.

Как я понимаю, он работает, потому что он заменяет функцию succ на helper функцию внутри декоратора (которая переопределяется каждый раз, когда она украшает функцию), поэтому succ = helper и succ.calls = helper.calls . (хотя, конечно, помощник имени определяется только в пространстве имен декоратора)

Поскольку я это понимаю (исправьте меня, если я ошибаюсь), выполняемый вами порядок:

  1. Зарегистрировать call_function .
  2. Зарегистрировать succ .
  3. При регистрации интерпретатора функций succ находит декоратор, поэтому он выполняет call_function .
  4. Функция возвращает объект, который является функцией ( helper ). И добавляет к этим calls поля объекта.
  5. Теперь ваша функция succ была назначена helper . Поэтому, когда вы вызываете свою функцию, вы на самом деле вызываете helper функцию, завернутую в декоратор. Таким образом, каждое поле, которое вы добавляете к своей вспомогательной функции, доступно снаружи, обращаясь к succ потому что эти 2 переменные относятся к одной и той же вещи.
  6. Поэтому, когда вы вызываете succ() это в основном то же самое, если вы будете делать helper(*args, **argv)
def helper(x): helper.calls += 1 return 2 helper.calls = 0 def call_counter(func): return helper @call_counter def succ(x): return x + 1 if __name__ == '__main__': print(succ == helper) # prints true. 

Когда вы украшаете функцию, которую вы “заменяете”, вы работаете с оберткой.

В этом примере после украшения, когда вы вызываете succ вы на самом деле называете helper . Поэтому, если вы подсчитываете звонки, вам нужно увеличить helper вызовы.

Вы можете проверить, что, как только вы украшаете функцию, имя привязывается к обертке, проверяя атрибут __name__ функции украшенного:

def call_counter(func): def helper(x): helper.calls += 1 return func(x) helper.calls = 0 return helper @call_counter def succ(x): return x + 1 print(succ.__name__) >>> 'helper' 

Источник

Как подсчитать общее количество вызовов метода класса в Python?

Язык программирования 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() # Результат

Но это поможет мне узнать, сколько раз каждый экземпляр класса, т.е. каждый корабль, был переименован:

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>
Code language: PHP (php)

Мне нужно подсчитать общее количество вызовов метода класса, а не общее количество вызовов для каждого экземпляра. Я знаю, что одним из способов может быть накопление всех кораблей и сложение атрибута 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()

Вывод этого кода соответствует требованиям:

'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>
Code language: JavaScript (javascript)

Поэтому рекомендуются следующие шаги:

  1. Добавьте статическую переменную с начальным значением 0.
  2. Реализуйте метод класса, который при вызове только увеличивает статическую переменную с предыдущего значения.
  3. Реализуйте метод-декоратор, внутренняя функция которого просто вызывает метод класса из предыдущего пункта.
    • Увеличение счетчика в этой внутренней функции не работает.
  4. Используйте вышеупомянутый декоратор для декорирования функции, вызовы которой мы хотим подсчитать.

Источник

Как подсчитать количество вызова функции Python

Как подсчитать количество вызова функции и как вывести. Как это сделать с помощью декоратора и как с помощью логирования, если это возможно и какие есть еще способы.

Важно: Без внешнего кода и глобальных переменных.

Ответы (3 шт):

counter = 0 def your_f(): global counter counter += 1 # now do your stuff . print(counter) # how many your_f was called 
def your_f(): your_f.counter += 1 your_f.counter = 0 for i in range(3): your_f() print(your_f.counter) 
class CountFunction(): def __init__(self, function): self.function = function self.counter = 0 def define(self, function): ''' Defines a function. Use external function or lambda. ''' self.function = function def count(self): ''' Returns how many it was called ''' return self.counter def reset(self): ''' Resets counter ''' self.counter = 0 def __call__(self, *args, **kwargs): '''Call''' self.counter += 1 return self.function(*args, **kwargs) 

Вот таким декоратором делаем иньекцию переменной счётчика в функцию:

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] 

Источник

Читайте также:  Permission denied access to index php denied
Оцените статью