Python中实现参数类型检查的简单方法
Python是一门弱类型语言,很多从C/C++转过来的朋友起初不是很适应。比如,在声明一个函数时,不能指定参数的类型。用C做类比,那就是所有参数都是void*类型!void类型强制转换在C++中被广泛地认为是个坏习惯,不到万不得已是不会使用的。
Python自然没有类型强制转换一说了,因为它是动态语言。首先,所有对象都从Object继承而来,其次,它有强大的内省,如果调用某个不存在的方法会有异常抛出。大多数情况,我们都不需要做参数类型栓查,除了一些特殊情况。例如,某个函数接受一个str类型,结果在实际调用时传入的是unicode,测试过程中又没有代码覆盖到,这样问题就比较严重了。解决方法也很简单,借助Python的内省,很容易就能判断出参数的类型。但是每个地方都写检查代码会很累赘,何况它带来的实际价值并不高。一个好的解决方法是使用装饰器。
'''
>>>NONE,MEDIUM,STRONG=0,1,2
>>>
>>>@accepts(int,int,int)
...defaverage(x,y,z):
...return(x+y+z)/2
...
>>>average(5.5,10,15.0)
TypeWarning:'average'methodaccepts(int,int,int),butwasgiven
(float,int,float)
15.25
'''
defaccepts(*types,**kw):
"""Functiondecorator.Checksthatinputsgiventodecoratedfunction
areoftheexpectedtype.
Parameters:
types--Theexpectedtypesoftheinputstothedecoratedfunction.
Mustspecifytypeforeachparameter.
kw--Optionalspecificationof'debug'level(thisistheonlyvalid
keywordargument,noothershouldbegiven).
debug=(0|1|2)
"""
ifnotkw:
#defaultlevel:MEDIUM
debug=1
else:
debug=kw['debug']
try:
defdecorator(f):
defnewf(*args):
ifdebug==0:
returnf(*args)
assertlen(args)==len(types)
argtypes=tuple(map(type,args))
ifargtypes!=types:
msg=info(f.__name__,types,argtypes,0)
ifdebug==1:
print>>sys.stderr,'TypeWarning:',msg
elifdebug==2:
raiseTypeError,msg
returnf(*args)
newf.__name__=f.__name__
returnnewf
returndecorator
exceptKeyError,key:
raiseKeyError,key+"isnotavalidkeywordargument"
exceptTypeError,msg:
raiseTypeError,msg
definfo(fname,expected,actual,flag):
"""Conveniencefunctionreturnsnicelyformattederror/warningmsg."""
format=lambdatypes:','.join([str(t).split("'")[1]fortintypes])
expected,actual=format(expected),format(actual)
msg="'%s'method"%fname\
+("accepts","returns")[flag]+"(%s),but"%expected\
+("wasgiven","resultis")[flag]+"(%s)"%actual
returnmsg
本质上讲,这也是一种运行时检查,但效果已经不错了。
更多有趣的装饰器的使用,可以参考这篇文章http://wiki.python.org/moin/PythonDecoratorLibrary