简体   繁体   English

Python Decorator类:如何正确计数with块中的函数调用

[英]Python Decorator Class: How to correctly count function calls in a with block

I am writing a simple decorator class in python that counts function calls. 我在python中编写一个简单的装饰器类,该类对函数调用进行计数。 So far my code is able to count function calls correctly, even in a with block. 到目前为止,即使在with块中,我的代码也能够正确计数函数调用。 My issue is that I also want to keep track of how many times I call decorated functions inside of a context manager (to the best of my understanding). 我的问题是,我还想跟踪在上下文管理器中调用装饰函数的次数(据我所知)。

Here is how the class can be used/tested: 这是使用/测试类的方法:

@fcount2
def f(n):
    return n+2

for n in range(5):
    print f(n)   
print 'f count =',f.count

def foo(n):
    return n*n

with fcount2(foo) as g:
    print g(1)
    print g(2)
print 'g count =',g.count
print 'f count =',f.count

with fcount2(f) as g:
    print g(1)
    print g(2)
print 'g count =',g.count
print 'f count =',f.count

with f:
    print f(1)
    print g(2)
print 'g count =',g.count
print 'f count =',f.count 

And here is the expected output using my class and the above code: 这是使用我的课程和上面的代码的预期输出:

2
3
4
5
6
f count = 5
1
4
with block count = 2
g count = 2
f count = 5
3
4
with block count = 2
g count = 2
f count = 7
3
4
with block count = 2
g count = 3
f count = 9

Here is my code, which does everything correctly except for the 'with block count' statements: 这是我的代码,除了“ with block count”语句外,它可以正确执行所有操作:

class fcount2(object):
    def __init__(self, inner_func):
        self.inner_func = inner_func
        self.count = 0
        self.block_count =0
    def __call__(self, *args, **kwargs):
        self.count += 1
        return self.inner_func(*args, **kwargs)
    def __enter__(self):
        self.block_count += 1
        return self
    def __exit__(self, exception_type, exception_value, tb):
        print "with block count: " + str(self.block_count)
        if exception_type is not None:
            return False
        return self

So what am I doing wrong? 那我在做什么错? Can you guys help out or at least point in me in the right direction so I can understand with blocks enough to get this working? 你们可以帮忙或至少我点了正确的方向,所以我可以足够得到这个工作块明白了吗? I have tried a handful of things including static properties but nothing seems to work. 我已经尝试了一些东西,包括静态属性,但似乎没有任何效果。 I am relatively new to python so the nuances escape me. 我是python的新手,所以细微差别使我逃脱了。

Edit- this is the output from the current program. 编辑-这是当前程序的输出。

2
3
4
5
6
f count = 5
1
4
with block count: 1
g count = 2
f count = 5
3
4
with block count: 1
g count = 2
f count = 7
3
4
with block count: 1
g count = 3
f count = 9

Each with statement creates a new instance of fcount2 so each instance only has one block_count - I don't have an answer but some additions to your code will illustrate what is happening. 每个with语句都会创建一个新的fcount2实例,因此每个实例只有一个block_count我没有答案,但是对您的代码的一些补充将说明正在发生的事情。

class fcount2(object):
    def __init__(self, inner_func):
        self.inner_func = inner_func
        self.count = 0
        self.block_count =0
    def __call__(self, *args, **kwargs):
        self.count += 1
        return self.inner_func(*args, **kwargs)
    def __enter__(self):
        print 'with block entered - id(self):', id(self)
        self.block_count += 1
        return self
    def __exit__(self, exception_type, exception_value, tb):
        print "with block exit - block count: " + str(self.block_count)
        if exception_type is not None:
            return False
        return self

sep = '*************************\n'
@fcount2
def f(n):
    return n+2

for n in range(5):
    print f(n)   
print 'f count =',f.count, ' | id(f):', id(f)

def foo(n):
    return n*n

print sep
with fcount2(foo) as g:
    print g(1), ' | id(g):', id(g)
    print g(2), ' | id(g):', id(g)
print 'g count =',g.count, ' | id(g):', id(g)
print 'f count =',f.count, ' | id(f):', id(f)

print sep
with fcount2(f) as g:
    print g(1), ' | id(g):', id(g)
    print g(2), ' | id(g):', id(g)
print 'g count =',g.count, ' | id(g):', id(g)
print 'f count =',f.count, ' | id(f):', id(f)

print sep
with f:
    print f(1), ' | id(f):', id(f)
    print g(2), ' | id(g):', id(g)
print 'g count =',g.count, ' | id(g):', id(g)
print 'f count =',f.count, ' | id(f):', id(f)

>>> 
2
3
4
5
6
f count = 5  | id(f): 66567888
*************************

with block entered - id(self): 66585136
1  | id(g): 66585136
4  | id(g): 66585136
with block exit - block count: 1
g count = 2  | id(g): 66585136
f count = 5  | id(f): 66567888
*************************

with block entered - id(self): 66587152
3  | id(g): 66587152
4  | id(g): 66587152
with block exit - block count: 1
g count = 2  | id(g): 66587152
f count = 7  | id(f): 66567888
*************************

with block entered - id(self): 66567888
3  | id(f): 66567888
4  | id(g): 66587152
with block exit - block count: 1
g count = 3  | id(g): 66587152
f count = 9  | id(f): 66567888
>>> 

The solution to your problem may be to have a class attribute that keeps track of all the instances of fcount2 , similar to the example in the PythonDecoratorLibrary 解决问题的方法可能是拥有一个类属性,该属性跟踪fcount2的所有实例,类似于PythonDecoratorLibrary中的示例


I played around a bit and came up with a solution, although I'm not sure it is what you are looking for, and it may not be the correct solution but it works for the scope of your examples. 尽管我不确定这不是您正在寻找的东西,但我花了些力气提出了一个解决方案,虽然它可能不是正确的解决方案,但它适用于示例范围。

The class adds attributes to the function it decorates, calls are accumulated in the function attributes, logic differentiates calls within a managed context , and instance properties refer to the function attributes. 该类向其装饰的函数添加属性,调用累积在函数属性中,逻辑区分托管上下文中的调用,实例属性引用函数属性。

class fcount2(object):
    def __init__(self, inner_func):
        self.inner_func = inner_func
        if not hasattr(self.inner_func, 'count'):
            self.inner_func.count = 0
        if not hasattr(self.inner_func, 'block_count'):
            self.inner_func.block_count = 0
        self.context_manager = False
    def __call__(self, *args, **kwargs):
        if self.context_manager:
            self.inner_func.block_count += 1
        else:
            self.inner_func.count += 1
        return self.inner_func(*args, **kwargs)
    def __enter__(self):
        self.context_manager = True
        return self
    def __exit__(self, exception_type, exception_value, tb):
        if exception_type is not None:
            return False
        self.context_manager = False
        return self
    @property
    def count(self):
        return self.inner_func.count
    @property
    def block_count(self):
        return self.inner_func.block_count

Usage: 用法:

@fcount2
def f(n):
    return n+2

for n in range(5):
    print f(n),
print 'f.count =',f.count

@fcount2
def foo(n):
    return n*n

print sep, 'with foo as g: ...'
with foo as g:
    print g(1), g(2)
print 'foo.count =',foo.count, ' | foo.block_count:', foo.block_count
print 'f.count =',f.count, ' | f.block_count:', f.block_count

print sep, 'with f as g: ...'
with f as g:
    print g(1), g(2)
print 'foo.count =',foo.count, ' | foo.block_count:', foo.block_count
print 'f.count =',f.count, ' | f.block_count:', f.block_count


>>> 
2 3 4 5 6 f.count = 5
*************************
with foo as g: ...
1 4
foo.count = 0  | foo.block_count: 2
f.count = 5  | f.block_count: 0
*************************
with f as g: ...
3 4
foo.count = 0  | foo.block_count: 2
f.count = 5  | f.block_count: 2
>>> 

Accessing the counts while in a managed context: 在托管上下文中访问计数

>>> with foo as g:
        for n in [1,2,3,4,5]:
            print 'g(n): {} | g.block_count: {} | foo.block_count: {}'.format(g(n), g.block_count, foo.block_count)


g(n): 1 | g.block_count: 3 | foo.block_count: 3
g(n): 4 | g.block_count: 4 | foo.block_count: 4
g(n): 9 | g.block_count: 5 | foo.block_count: 5
g(n): 16 | g.block_count: 6 | foo.block_count: 6
g(n): 25 | g.block_count: 7 | foo.block_count: 7
>>>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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