File: decorator0.py
"""
tracer function decorator examples
run me under 3.0 to make the 3rd example execute
"""
# add return, **, 3.0 print()
class tracer:
def __init__(self, func): # on @ decorator
self.calls = 0 # save func for later call
self.func = func
def __call__(self, *args, **kwargs): # on call to original function
self.calls += 1
print('call %s to %s' % (self.calls, self.func.__name__))
return self.func(*args, **kwargs)
if __name__ == '__main__':
@tracer
def spam(a, b, c): # same as: spam = tracer(spam)
print(a + b + c) # triggers tracer.__init__
@tracer
def eggs(x, y): # same as: eggs = tracer(eggs)
print(x ** y) # wraps eggs in a tracer object
spam(1, 2, 3) # really calls tracer instance: runs tracer.__call__
spam(a=4, b=5, c=6) # spam is an instance attribute
eggs(2, 16) # really calls tracer instance, self.func is eggs
eggs(4, y=4) # self.calls is per-function here (need 3.0 nonlocal)
print('')
###################################################################
# enclosing scopes, global shared by all wrapped functions
calls = 0
def tracer(func): # state via nested scope and global
def wrapper(*args, **kwargs): # instead of class attributes
global calls # calls is global, not per-function
calls += 1
print('call %s to %s' % (calls, func.__name__))
return func(*args, **kwargs)
return wrapper
if __name__ == '__main__':
@tracer
def spam(a, b, c): # same as: spam = tracer(spam)
print(a + b + c)
@tracer
def eggs(x, y): # same as: eggs = tracer(eggs)
print(x ** y)
spam(1, 2, 3) # really calls wrapper, bound to func
spam(a=4, b=5, c=6) # wrapper calls spam
eggs(2, 16) # really calls wrapper, bound to eggs
eggs(4, y=4) # global calls is not per-function here
print('')
###################################################################
# enclosing scopes, 3.0 nonlocal is per-function counter
#
# caveat: need to exec as string, else 'nolocal' is a
# syntax error in 2.6
#
import sys
if sys.version_info[0] == 3:
code = """
def tracer(func): # state via nested scope and nonlocal
calls = 0 # instead of class attrs or global
def wrapper(*args, **kwargs): # calls is per-function, not global
nonlocal calls
calls += 1
print('call %s to %s' % (calls, func.__name__))
return func(*args, **kwargs)
return wrapper
if __name__ == '__main__':
@tracer
def spam(a, b, c): # same as: spam = tracer(spam)
print(a + b + c)
@tracer
def eggs(x, y): # same as: eggs = tracer(eggs)
print(x ** y)
spam(1, 2, 3) # really calls wrapper, bound to func
spam(a=4, b=5, c=6) # wrapper calls spam
eggs(2, 16) # really calls wrapper, bound to eggs
eggs(4, y=4) # nonlocal calls is per-function here
"""
exec(code)
print('')