利用装饰器给python的函数加上类型限制

前言

做为一名python的脑残粉,请先跟我念一遍python大法好。java

其做为动态语言的灵活,简介的代码,确实在某些状况下确实比其余编程语言要好。但你有没有想过,有时这些灵活的语法,可能会形成一些糟糕的体验。尤为是针对新手,python易上手不假,但动态语言写得项目规模一大,其实比相对严谨的静态语言,更考验程序员的内力。python

哪怕你只是用过python写过一些初等的项目,那你可能也体验过如下这种状况。git

  • 莫名其妙的传错参数类型。
    python不须要显式声明参数类型,一样,什么样的变量均可以往函数里扔,包括函数(python支持函数式编程),这容易出现一个问题,若是一个函数不是本身设计的,你极可能网里面传了错误类型的参数。固然,这样大多数触发异常,由于传错类型意味着函数的一些操做该类型不支持。但有时传了错误的参数类型,却并不会触发异常(好比字符串相加和数字相加,以及一切对象的==判断),不会触发异常结果倒是错误的,这就意味着出现了问题更难肯定位置,甚至若是这个函数的返回值,再传进其余函数(假设叫B)时,当你发现获得结果错误时,你极可能觉得是B函数的逻辑设计出现了错误,从而花费大量的时间在错误的地方,使用python多数是对开发效率比较重要的场景,而极可能由于一个粗心,使得写代码的时间短了,结果将时间都花在找BUG上了。程序员

  • 进行操做以前忘记了转型
    典型的就是把参数类型为strint相加。或者把str传进range()里面。github

  • 列表越界
    python的列表相似于动态数组,没有长度的限制。虽然大多数咱们只需用for x in one_list便可完成对列表的访问,而不须要去考虑列表的长度。但其实还有一种情景,好比说一个列表(或者元组)中元素的次序是有意义的,好比说[name,age,sex]并且这多是某个函数动态生成的,好比爬虫爬取网站后从里面挑选信息后返回,这时,若是这个网站中age,sex的信息缺失,python可不会自动补充None下去,至少我没看见有人的函数或方法会考虑到这一点,而是直接给你返回[name],而若是你须要获取age,而直接访问下标为1的元素,则会触发异常。算法

相似的还有种种,固然并不是不可解决,好比足够多的assertisinstance,足够严谨的逻辑设计,枯燥但颇有必要的单元测试等等……但使用python不少时候就是为了加快开发效率,上面的这些措施显然太过麻烦。编程

断断续续的写了一两天,弄了几个装饰器来解决这些问题,下面开始分享一下。数组

什么是装饰器

须要了解pythopn中装饰器的基本概念,能够参考一下廖老师的py教程
点这里app

如何利用装饰器限制函数的参数和返回值

使用装饰器可使得一个函数外面加上某些操做而后在从新返回到你定义的函数名字指定的对象上。编程语言

说实话,我很难用言语描述出这种关系,直接上代码好了。

以使用装饰器限制函数参数类型为例:

装饰器的实现以下:

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

这时,咱们能够这么作:

@type_limit(int,int)
def test(x,y):
    return x+y

这个定义的过程发生了什么呢?上述代码等价于

temp = type_limit(int,int) #temp =  test_value_type
test = temp(test) #这是,test已经在原test上通过修饰,指向wrapper

而在wrapper中,最终会返回调用原test的结果,这个装饰器作的,只不过是在调用原test前,利用
isinstance进行了一遍类型检测而已。这样,咱们能够简单的模仿像javaC++这样的静态语言同样,在声明的时候就对参数类型进行限制了。

理解这个装饰器把握着一下几点:

  • 函数能够做为变量使用,便可以做为参数和返回值

  • 装饰器利用了函数内的函数,能够访问外层函数之间的一些变量从而对内层函数进行修饰。(好比对将要传进内层函数的参数进行检测等),从而实现对参数的类型进行限制。

理解这两点后,你能够自由的修改和拓展这些装饰器,若是你有更好的实现,记得在githubpull给我哦,github地址稍后给出。

其余相关的限制

除上述外,我仍是实现了其余限制:

  • 列表长度的限制,不足指定长度,自动补充指定元素。

  • 对二维列表的每一维列表进行长度限制,不足指定长度,自动补充指定元素。主要为某些算法进行限制。

  • 常量类Const,目测没有什么用

  • 对列表的每一元素的类型进行限制

后记

限于篇幅,其余的代码不一一在这里介绍,关键思路在上文已给出,其他代码开源在github上,若是须要,你能够直接拿去使用。不过记得不要滥用。

github地址

若有更好的建议和或不正确的地方,能够在本文或github下告知。

若有错别字……请忽略(^ ^)

相关文章
相关标签/搜索