前言
作为一名python的脑残粉,请先跟我念一遍python大法好。
其作为动态语言的灵活,简介的代码,确实在某些情况下确实比其他编程语言要好。但你有没有想过,有时这些灵活的语法,可能会造成一些糟糕的体验。尤其是针对新手,python易上手不假,但动态语言写得项目规模一大,其实比相对严谨的静态语言,更考验程序员的内力。
哪怕你只是用过python写过一些初等的项目,那你可能也体验过以下这种情况。
str
与int
相加。或者把str
传进range()
里面。for x in one_list
即可完成对列表的访问,而不需要去考虑列表的长度。但其实还有一种情景,比如说一个列表(或者元组)中元素的次序是有意义的,比如说[name,age,sex]
而且这可能是某个函数动态生成的,比如爬虫爬取网站后从里面挑选信息后返回,这时,如果这个网站中age,sex的信息缺失,python可不会自动补充None下去,至少我没看见有人的函数或方法会考虑到这一点,而是直接给你返回[name]
,而如果你需要获取age,而直接访问下标为1的元素,则会触发异常。类似的还有种种,当然并非不可解决,比如足够多的assert
和isinstance
,足够严谨的逻辑设计,枯燥但很有必要的单元测试等等……但使用python很多时候就是为了加快开发效率,上面的这些措施显然太过麻烦。
断断续续的写了一两天,弄了几个装饰器来解决这些问题,下面开始分享一下。
需要了解pythopn中装饰器的基本概念,可以参考一下廖老师的py教程
点这里
使用装饰器可以使得一个函数外面加上某些操作然后在重新返回到你定义的函数名字指定的对象上。
说实话,我很难用言语描述出这种关系,直接上代码好了。
以使用装饰器限制函数参数类型为例:
装饰器的实现如下:
12345678910111213141516171819 | def type_limit(*typeLimit,**returnType): def test_value_type(func): def wrapper(*param,**kw): length = len(typeLimit) if length != len(param): raise LimitError(\”The list of typeLimit and param must have the same length\”) for index in range(length): t = typeLimit[index] p = param[index] if not isinstance(p,t): raise LimitError(\”The param %s should be %s,but it\’s %s now!\”%(str(p),type(t()),type(p))) res = func(*param,**kw) if \”returnType\” in returnType: limit = returnType[\”returnType\”] if not isinstance(res,limit): raise LimitError(\”This function must return a value that is %s,but now it\’s %s\”%(limit,type(res) ) ) return res return wrapper return test_value_type |
假设我希望实现一个函数,实现两数求和,为了避免传进去的是两个字符串,造成字符串连接,我需要限制其类型都为int
这时,我们可以这么做:
123 | @type_limit(int,int)def test(x,y): return x+y |
这个定义的过程发生了什么呢?上述代码等价于
12 | temp = type_limit(int,int) #temp = test_value_typetest = temp(test) #这是,test已经在原test上经过修饰,指向wrapper |
而在wrapper中,最终会返回调用原test的结果,这个装饰器做的,只不过是在调用原test前,利用
isinstance
进行了一遍类型检测而已。这样,我们可以简单的模仿像java
,C++
这样的静态语言一样,在声明的时候就对参数类型进行限制了。
理解这个装饰器把握着一下几点:
理解这两点后,你可以自由的修改和拓展这些装饰器,如果你有更好的实现,记得在github
上pull
给我哦,github
地址稍后给出。
除上述外,我还是实现了其他限制:
限于篇幅,其他的代码不一一在这里介绍,关键思路在上文已给出,其余代码开源在github上,如果需要,你可以直接拿去使用。不过记得不要滥用。
github地址
如有更好的建议和或不正确的地方,可以在本文或github
下告知。
如有错别字……请忽略(^ ^)