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