Python装饰器类: How to correctly count function calls in a with block

标签 python class decorator with-statement

我正在用 python 编写一个简单的装饰器类来计算函数调用。到目前为止,我的代码能够正确计算函数调用,即使在 with block 中也是如此。我的问题是,我还想跟踪我在上下文管理器中调用装饰函数的次数(据我所知)。

以下是如何使用/测试该类:

@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 

这是使用我的类和上面的代码的预期输出:

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

这是我的代码,除了“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

那我做错了什么?你们能帮忙或者至少为我指明正确的方向,以便我能够理解足以让这个工作正常进行的 block 吗?我尝试了一些方法,包括静态属性,但似乎没有任何效果。我对 python 比较陌生,所以我无法理解其中的细微差别。

编辑 - 这是当前程序的输出。

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

最佳答案

每个 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
>>> 

问题的解决方案可能是使用一个类属性来跟踪 fcount2 的所有实例,类似于 PythonDecoratorLibrary 中的示例。


我尝试了一下并想出了一个解决方案,尽管我不确定这是否是您正在寻找的解决方案,并且它可能不是正确的解决方案,但它适用于您的示例范围。

类向它所修饰的函数添加属性,调用在函数属性中累积,逻辑区分托管上下文中的调用,实例属性引用函数属性。

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

用法:

@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
>>> 

在托管上下文中访问计数:

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

关于Python装饰器类: How to correctly count function calls in a with block,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28954638/

相关文章:

java - GWT 序列化和装饰器模式

python - pandas 列值和变量值字符串连接

python - 在 Python 中异步编写 CSV 文件

python - 如何用pymunk实现n体模拟?

python - 将一个 numpy 数组插入另一个数组而不必担心长度

c++ - 为什么在 C++ 中使用 Delete[] 会出现 Trace/Breakpoint 错误?

Ruby 类实例变量和继承

c++ - 变量或字段 'name of var'声明为void

Python:用装饰器计算递归函数的执行时间

jquery - Nodejs 函数中的域错误处理程序装饰器