Python的高阶函数、柯里化和装饰器

1. 高阶函数

1.1 高阶函数的概念

所谓的高阶函数,就是接受一个或多个函数作为参数,或者说输出的是一个函数。这两个条件满足一个,就算是高阶函数。

例如:

def plus1_1ist(iterable, func = lambda x: x+1):
    return [func(i) for i in iterable]

这个函数就是要在参数中传入一个函数,然后在内部应用这个函数。

1.2 一些高阶内建函数

1.2.1 filter(function,iterable)

它是一个筛选元素的函数

给一个function和iterable,选出iterable里符合条件的元素,返回一个迭代器,惰性求值。

也就是说,如果这个function的返回值为True,就返回这个元素,如果是False,就跳过。

例如,能被2整除,也就是function返回值为True的元素返回:

i = filter(lambda x : x%2==0, range(10))
list(i)
[0, 2, 4, 6, 8]

1.2.2 map(function, iterable)

它是一个转化元素的函数

把iterable的元素一个个拿出来,按function做改变,元素一个都不少的返回。

这个函数返回的是迭代器,惰性求值。

map()后面的函数返回的必须是一个值,不能是多个值,但是后面用于计算的可迭代对象可以是多个,例如:

i2 = map(lambda x: x+1, range(5)) #这用的是一个iterable
list(i2)
[1, 2, 3, 4, 5]

i3 = map(lambda x, y: x+y, 'abc','qwe') #这使用两个iterable
list(i3)
['aq', 'bw', 'ce']

2. 柯里化

比如一个函数要求一次输入两个参数,通过柯里化,可以变成依次输入两个参数。

fn(x, y) -> fn(x)(y)

它的原理就是,把这个函数做成嵌套函数,外层函数的返回值是内层函数,这样,外层函数调用完,返回的是一个函数,然后能接着调用。

例子:

def plus1(x, y):
    return x + y

进行柯里化:

def puls2(x):
    def _plus(y): #注意,这里有一个闭包,内层函数用了外层函数的变量
        return x + y
    return _plus


plus1(1, 2) #是一样的
plus2(1)(2)

3. Python的装饰器

3.1 装饰器的概念和作用

因为要用到上面两个知识,所以放在一起说。

装饰器本来是一门技术,但是在python中发扬光大了。

如果有一个情况,要定义一个函数,功能是做加法,哐哐哐就写出来了,但是客户过几天又说,唉你能不能给这个函数加一个报时功能,我想知道这个函数产生返回值的时间。如果再去改代码的话,相当于把代码死死写在函数里,这叫做硬编码,不具有适用性,如果客户要在替别的要求,还得改,你又不能打他。

但是如果运用装饰器,就能做到了业务功能和附加功能的分离,并且解决了业务函数的参数调用的问题。

3.2 装饰器的基本结构

如下:

def decorator(fn):
    def wrapper(*args, **kwargs):
        .... # 这里写要附加的功能
        ....
        ret = fn(*args, **kwargs)
        ....
        ....
        return ret
    ....
    ....
    return wrapper

大概就是定义一个名字叫decorator的装饰器,把要被装饰的函数传参传进去,然后它的返回值是一个包装函数(先这么叫),这个包装函数是在内部定义的,然后在包装函数的内部,写出想要的附加功能,执行fn,并返回结果。

装饰器的使用方式如下,一个@装饰器名,下面是定义的函数:

@decorator
def fn():
    ....

这个格式相当于:fn = decorator(fn)。就是说函数名是没改变,但是这个函数内容已经变了,你大爷已经不是你大爷了,已经加入了我们想要附加的功能。

4. 带参装饰器和不带参装饰器

4.1 不带参装饰器

就像上面举的例子一样,调用时一直输入:

@decorator
def .... 

就行。

4.2 带参装饰器

4.2.1 举例

要以某个自定义的参数参加到装饰器的执行中,来给下面的函数增加附加功能。

就是说,要在装饰器的后面写入一些参数,也就是上面那个例子,

首先介绍函数有两个属性 .__name__ 和.__doc__,前者是函数的名字,后者是函数的帮助(一般写在函数的第一行):

def plus(x, y):
    """
    x + y = ?
    """
    return x + y

plus.__doc__
'\n    x + y = ?\n    ' #注意换行符是哪出来的

plus.__name__
'plus'

我们用装饰器给函数添加附加功能之后,其实是偷梁换柱,把函数替换成了另一个函数。但是这样,这个函数的函数名和注释文档就会变成装饰器内部函数的名字和注释文档。

def outer(fn): #这是一个装饰器
    def inner(*args, **kwargs):
        """decorator"""
        print('in_decorator_test')
        ret = fn(*args, **kwargs) 
        return ret #如果不准备对返回值进行修改,可以直接写在return里
    return inner

定义一个函数:

def add(x=1, y=1):
    """add.doc"""
    return x+y

add.__name__
'add'

add.__doc__
'add.doc'

然后把这个函数用装饰器添加附加功能(就是print一条测试语句):

@outer
def add__test(x=1, y=1):
    """add.doc"""
    return x+y

再调用add()

add()
'in_decorator_test'    #这是装饰器附加的功能
2    #这是add函数原本计算的结果

add.__name__
'inner'

add.__doc__
'decorator'

这就是偷梁换柱的遗留问题,所以我们要把这个属性改一改:

def copy_properties(pre_func):
    def _copy_wrapper(dest_func): 
        dest__func.__name__ = pre_func.__name__
        dest_func.__doc__ = pre_func.__doc__
        return dest_func
    return _copy_wrapper

以上面改add函数的name和doc为例,调用时:

@copy_properties(add) #add_test = copy_properties(add)
def add_test(x=1, y=1):
    """add.doc"""
    return x+y

这里,add相当于pre_func,add_test相当于dest_func,这个装饰器没有执行函数,只是换了函数的一些属性,返回的是函数,可以接着调用。

4.2.2 技巧

我们可以通过一些传参,来控制装饰器的条件,来应对不同的需求

def decorator(fn):
    def inner():
        ....
        ....
        return fn()
    return inner

如果想传入一些参数,来控制这个装饰器的作用,可以再嵌套一层函数:

def decorator(params) #给装饰器用的参数
    def _decorator(fn): # @符号提上来的函数
        def inner():
            ....  #装饰器部分,其中用到params
            ....
            return fn()
        return inner
    return _decorator

调用的时候:

@decorator(params)
def function():
    ....
    return ....

这样就能改变装饰器。

猜你喜欢

转载自blog.csdn.net/LittleHuang950620/article/details/82155597