Как создать счетчик функций для конкретного экземпляра?

#python #class #counter #instance-variables

#python #класс #счетчик #переменные экземпляра

Вопрос:

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

Мы можем настроить счетчик для простой функции:

 def foo1():
    pass

foo1.count = 0
foo1.count  =1
print(foo1.count)
 

Теперь давайте обратимся к методам класса:

 class A:
    def foo2(self):
        print('Hi')
 

мы также можем установить счетчик здесь

 A.foo2.count = 0
a = A()
b = A()
 

но этот счетчик НЕ зависит от конкретного экземпляра

 A.foo2.count  = 1
print(a.foo2.count)
print(b.foo2.count)
 

невозможно установить счетчик для метода ЭКЗЕМПЛЯРА:

 a.foo2.count  = 1
 

и если мы будем использовать __func__ , это будет эквивалентно изменению A.foo2.count :

 a.foo2.__func__.count  = 1
print(b.foo2.count)
print(a.foo2.count)
print(A.foo2.count)
 

Вопрос: как сделать foo2.count конкретный экземпляр конкретным? Так что a и b может иметь несколько значений foo2.count ?

Пожалуйста, обратите внимание: я спрашиваю об атрибуте функции, а не об атрибуте класса.

Ответ №1:

Может быть, это тогда?

 from collections import defaultdict


def foo1():
    print('Hi')
foo1.count = defaultdict(int)

class A:

    def foo2(self):
        foo1()
        foo1.count[self]  = 1



a = A()
b = A()

a.foo2()
a.foo2()
b.foo2()

print(foo1.count)
 

вывод:

 Hi
Hi
Hi
defaultdict(<class 'int'>, {<__main__.A object at 0x000001E59759ABE0>: 2, <__main__.A object at 0x000001E596AFCD30>: 1})
 

Комментарии:

1. Нравится ваша идея, но что произойдет, если экземпляр класса тяжелый? Допустим, вы создали 1 миллион экземпляров, тогда этот словарь может быть большим? Или это не имеет значения, потому что size (hash (self)) << размер (self) ?

Ответ №2:

используйте __init__ метод:

 class A:

    def __init__(self):
        self.count_foo2 = 0

    def foo2(self):
        print('Hi')
        self.count_foo2  = 1 


a = A()
b = A()

a.foo2()

print(a.count_foo2)
print(b.count_foo2)
 

Вывод:

 Hi
1
0
 

Комментарии:

1. count_foo2 является атрибутом экземпляра класса, а не функции. Я спрашивал об атрибуте функции.

2. вы должны решить, является ли count атрибутом функции или экземпляра класса… Судя по всему, ваше использование указывает на то, что это должен быть атрибут экземпляра. Почему бы вам не рассказать нам о реальной проблеме, которую вы пытаетесь решить, это звучит как проблема XY…

3. Я хотел бы создать декоратор класса «счетчик», который отслеживает, сколько раз выполнялась функция, но только «внутри» этого конкретного экземпляра класса. Я надеялся избежать создания атрибутов экземпляра (как в вашем примере) и работать с функциями напрямую

Ответ №3:

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

 def fn_exec_count(base_fn):
    def new_fn(self, *args, **kwargs):
        fn_count_attr = 'fn_'   base_fn.__name__   '_count'
        if not hasattr(self, fn_count_attr):
            setattr(self, fn_count_attr, 1)
        else:
            setattr(self, fn_count_attr, getattr(self, fn_count_attr)   1)
        base_fn(self, *args, **kwargs)
    new_fn.__name__ = base_fn.__name__
    return new_fn

class A:
    @fn_exec_count
    def foo(self):
        fn_count_attr = 'fn_'   A.foo.__name__   '_count' 
        print(f'Value of counter: {getattr(self, fn_count_attr, None)}')


a1 = A()
a1.foo()
a1.foo()
a2 = A()
a2.foo()
a1.foo()
 

Выводит:

 Value of counter: 1
Value of counter: 2
Value of counter: 1
Value of counter: 3
 

Редактировать: кража из ответа Жюльена для использования defaultdict делает это немного менее подробным.

 def fn_exec_count_dict(base_fn):
    def new_fn(self, *args, **kwargs):
        if not hasattr(self, 'fn_exec_count'):
            setattr(self, 'fn_exec_count', defaultdict(int))
        self.fn_exec_count[base_fn.__name__]  = 1
        base_fn(self, *args, **kwargs)
    new_fn.__name__ = base_fn.__name__
    return new_fn

class A:
    @fn_exec_count_dict
    def foo(self):
        print(f'Value of counter: {self.fn_exec_count[A.foo.__name__]}')

# ...
 

Комментарии:

1. Спасибо за ваш ответ. Я должен согласиться с Жюльеном, потому что он был первым, кто придумал способ создания функции, а не атрибута класса, но я ценю вашу помощь и усилия!