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