简体   繁体   中英

How to create instance specific function counter?

I would like to track function count inside the class but such that this count is different for each instance of the class.

We can set up a counter on a simple function:

def foo1():
    pass

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

Now let`s turn to class methods:

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

we can set counter here as well

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

but this count is NOT instance specific

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

it is not possible to set count for an INSTANCE method:

a.foo2.count += 1

and if we use __func__ it will be equivalent to changing A.foo2.count :

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

Question: how to make foo2.count INSTANCE specific? So that a and b could have multiple values of foo2.count ?

Please note: I am asking about function attribute, not class attribute.

use the __init__ method:

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)

Output:

Hi
1
0

Maybe this then?

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)

output:

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

Here is my attempt. I am not sure this is entirely what you want as in one of your comments you indicated that you wanted specifically function instance attributes (?). This however seems to behave the way you described. It has the benefit of working regardless of how many methods for which you want this behavior to occur.

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()

Outputs:

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

Edit: Stealing from Julien's answer to use defaultdict makes this slightly less verbose.

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__]}')

# ...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM