跟蹤調(diào)用
class tracer: # State via instance attributes
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)
用例:
@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)
output:
call 1 to spam
6
call 2 to spam
15
call 1 to eggs
65536
call 2 to eggs
256
計(jì)時(shí)調(diào)用
import time
def timer(label='', trace=True): # On decorator args: retain args
class Timer:
def __init__(self, func): # On @: retain decorated func
self.func = func
self.alltime = 0
def __call__(self, *args, **kargs): # On calls: call original
start = time.clock()
result = self.func(*args, **kargs)
elapsed = time.clock() - start
self.alltime += elapsed
if trace:
format = '%s %s: %.5f, %.5f'
values = (label, self.func.__name__, elapsed, self.alltime)
print(format % values)
return result
return Timer
用例:
@timer(label='[CCC]==>')
def listcomp(N): # Like listcomp = timer(...)(listcomp)
return [x * 2 for x in range(N)] # listcomp(...) triggers Timer.__call__
@timer(trace=True, label='[MMM]==>')
def mapcall(N):
return map((lambda x: x * 2), range(N))
for func in (listcomp, mapcall):
print('')
result = func(5) # Time for this call, all calls, return value
func(50000)
func(500000)
func(1000000)
print(result)
print('allTime = %s' % func.alltime) # Total time for all calls
print('map/comp = %s' % round(mapcall.alltime / listcomp.alltime, 3))
output:
[CCC]==> listcomp: 0.00003, 0.00003
[CCC]==> listcomp: 0.00640, 0.00643
[CCC]==> listcomp: 0.08687, 0.09330
[CCC]==> listcomp: 0.17911, 0.27241
allTime = 0.272407666337
[MMM]==> mapcall: 0.00004, 0.00004
[MMM]==> mapcall: 0.01340, 0.01343
[MMM]==> mapcall: 0.13907, 0.15250
[MMM]==> mapcall: 0.27907, 0.43157
[0, 2, 4, 6, 8]
allTime = 0.431572169089
map/comp = 1.584
[0, 2, 4, 6, 8]
單例
class singleton:
def __init__(self, aClass): # On @ decoration
self.aClass = aClass
self.instance = None
def __call__(self, *args): # On instance creation
if self.instance == None:
self.instance = self.aClass(*args) # One instance per class
return self.instance
用例:
@singleton
class Test:
def __init__(self,**args):
self.a = args["a"]
self.b = args["b"]
def main():
t1 = Test(**{"a":1,"b":2})
print("a:",t1.a,"b:",t1.b)
t2 = Test()
print("a:",t2.a,"b:",t2.b)
output:
('a:', 1, 'b:', 2)
('a:', 1, 'b:', 2)
跟蹤對(duì)象接口
def Tracer(aClass): # On @ decorator
class Wrapper:
def __init__(self, *args, **kargs): # On instance creation
self.fetches = 0
self.wrapped = aClass(*args, **kargs) # Use enclosing scope name
def __getattr__(self, attrname):
print('Trace: ' + attrname) # Catches all but own attrs
self.fetches += 1
return getattr(self.wrapped, attrname) # Delegate to wrapped obj
return Wrapper
用例:
@Tracer
class Spam: # Spam = Tracer(Spam)
def display(self): # Spam is rebound to Wrapper
print('Spam!' * 8)
@Tracer
class Person: # Person = Tracer(Person)
def __init__(self, name, hours, rate): # Wrapper remembers Person
self.name = name
self.hours = hours
self.rate = rate
def pay(self): # Accesses outside class traced
return self.hours * self.rate # In-method accesses not traced
food = Spam() # Triggers Wrapper()
food.display() # Triggers __getattr__
print([food.fetches])
bob = Person('Bob', 40, 50) # bob is really a Wrapper
print(bob.name) # Wrapper embeds a Person
print(bob.pay())
print('')
sue = Person('Sue', rate=100, hours=60) # sue is a different Wrapper
print(sue.name) # with a different Person
print(sue.pay())
print(bob.name) # bob has different state
print(bob.pay())
print([bob.fetches, sue.fetches]) # Wrapper attrs not traced
output:
Trace: display
Spam!Spam!Spam!Spam!Spam!Spam!Spam!Spam!
[1]
Trace: name
Bob
Trace: pay
2000
Trace: name
Sue
Trace: pay
6000
Trace: name
Bob
Trace: pay
2000
[4, 2]
管理函數(shù)和類
def register(label="",registerd = False):
def onRegister(func):
key = label if label else func.__name__
if registerd:
registerMap[key] = func
return func
return onRegister
用例:
@register(registerd=True)
def test1():
print("test1")
@register(label="modify")
def test2():
print("test2")
@register(label="test1",registerd=True)
def test3():
print("test3")
print getInstance("test1")
print getInstance("test2")
print getInstance("modify")
output:
<function test3 at 0x00000000024C1DD8>
ERROR:root:no attribute registered!
None
ERROR:root:no attribute registered!
None
驗(yàn)證函數(shù)參數(shù)
def rangetest(**argchecks):
def onDecorator(func):
code = func.__code__
allargs = code.co_varnames[:code.co_argcount]
funcname = func.__name__
def onCall(*pargs, **kargs):
positionals = list(allargs)
positionals = positionals[:len(pargs)]
for (argname, (low, high)) in argchecks.items():
# For all args to be checked
if argname in kargs:
# Was passed by name
if kargs[argname] < low or kargs[argname] > high:
errmsg = '{0} argument "{1}" not in {2}..{3}'
errmsg = errmsg.format(funcname, argname, low, high)
raise TypeError(errmsg)
elif argname in positionals:
# Was passed by position
position = positionals.index(argname)
if pargs[position] < low or pargs[position] > high:
errmsg = '{0} argument "{1}" not in {2}..{3}'
errmsg = errmsg.format(funcname, argname, low, high)
raise TypeError(errmsg)
else:
# Assume not passed: default
if True:
print('Argument "{0}" defaulted'.format(argname))
return func(*pargs, **kargs) # OK: run original call
return onCall
return onDecorator
用例:
class Person:
def __init__(self, name, job, pay):
self.job = job
self.pay = pay
# giveRaise = rangetest(...)(giveRaise)
@rangetest(percent=(0.0, 1.0)) # percent passed by name or position
def giveRaise(self, percent):
self.pay = int(self.pay * (1 + percent))
bob = Person('Bob Smith', 'dev', 100000)
sue = Person('Sue Jones', 'dev', 100000)
bob.giveRaise(.10)
sue.giveRaise(percent=.20)
print(bob.pay, sue.pay)
bob.giveRaise(1.10)
bob.giveRaise(percent=1.20)
# Test omitted defaults: skipped
output:
Argument "percent" defaulted
(100000, 120000)
Traceback (most recent call last):
File "d:/huyaowen/workspace/demo/rangetest.py", line 54, in <module>
main()
File "d:/huyaowen/workspace/demo/rangetest.py", line 50, in main
bob.giveRaise(percent=1.20)
File "d:/huyaowen/workspace/demo/rangetest.py", line 18, in onCall
raise TypeError(errmsg)
TypeError: giveRaise argument "percent" not in 0.0..1.0
參數(shù)類型檢測
def typetest(**argchecks):
def onDecorator(func):
code = func.__code__ # __code__ 返回已編譯的函數(shù)對(duì)象
allargs = code.co_varnames[:code.co_argcount]
funcname = func.__name__
def onCall(*pargs, **kargs):
positionals = list(allargs)[:len(pargs)]
for (argname, expectedtype) in argchecks.items():
# 檢測參數(shù)是否在關(guān)鍵字參數(shù)中
if argname in kargs:
if not isinstance(kargs[argname], expectedtype):
errmsg = '{0} argument "{1}" type is {2} ,not the expected type {3}'
errmsg = errmsg.format(funcname, argname, type(kargs[argname]), expectedtype)
raise TypeError(errmsg)
# 檢測參數(shù)是否在位置參數(shù)中
elif argname in positionals:
position = positionals.index(argname)
if not isinstance(pargs[position], expectedtype):
errmsg = '{0} argument "{1}" type is {2} ,not the expected type {3}'
errmsg = errmsg.format(funcname, argname, type(pargs[position]), expectedtype)
raise TypeError(errmsg)
else:
pass
return func(*pargs, **kargs)
return onCall
return onDecorator
用例:
@typetest(a=int, c=float)
def func(a, b, c, d):
print(a,b,c,d)
func(1, 2, 3.0, 4) # Okay
func('spam', 2, 99, 4)
output:
(1, 2, 3.0, 4)
Traceback (most recent call last):
File "d:/huyaowen/workspace/demo/typetest.py", line 38, in <module>
main()
File "d:/huyaowen/workspace/demo/typetest.py", line 35, in main
func('spam', 2, 99, 4)
File "d:/huyaowen/workspace/demo/typetest.py", line 22, in onCall
raise TypeError(errmsg)
TypeError: func argument "a" type is <type 'str'> ,not the expected type <type 'int'>