1. 主页
  2. Python基础到高级
  3. 装饰器
  4. 定义

定义

高阶函数的定义

返回函数或者参数是函数的函数,就是高阶函数

在Python中函数是一等对象(first class),因为函数也是对象,并且它可以像普通对象一样赋值,作为参数,作为返回值。

>>> def counter(base):    这是一个闭包的结构,也是一个典型的高阶函数,返回值是函数
...     def inc(x=1):
...         nonlocal base
...         base += x
...         return base
...     return inc
... 
>>> inc = counter(3)
>>> inc()
4
>>> inc(4)
9

高阶函数的应用场景

将匿名函数lambda当做参数来传递,会大大减少代码量,提升代码运行速度,清晰代码的阅读

In [6]: def sort(it,cmp=lambda a,b:a<b):
   ...:     ret = []
   ...:     for x in it:
   ...:         for i,v in enumerate(ret):
   ...:             if cmp(x,v):
   ...:                 ret.insert(i,x)
   ...:                 break
   ...:         else:
   ...:             ret.append(x)
   ...:     return ret
   ...: 

In [7]: sort([1,4,2,3,6,5])
Out[7]: [1, 2, 3, 4, 5, 6]

In [8]: sort([1,4,2,3,6,5],lambda a,b:a>b)
Out[8]: [6, 5, 4, 3, 2, 1]

使用函数作为参数,并且返回值也是函数的场景,也叫做装饰器

实现一个获取函数返回时间的高阶函数
In [3]: def logger(fn):   函数作为返回值,封装了fn
   ...:     def wrap(*args,**kwargs):   可变参数
   ...:         start = datetime.datetime.now()  函数的开始时间
   ...:         ret = fn(*args,**kwargs)   参数解构
   ...:         end = datetime.datetime.now()   函数的结束时间
   ...:         print('call {} took {}'.format(fn.__name__,end-start))
   ...:         return ret
   ...:     return wrap
   ...: 

In [4]: def add(x,y):
   ...:     return x + y
   ...: 

In [5]: loged_add = logger(add)

In [6]: loged_add(3,5)   将add函数的执行时间打印出来,返回值也打印出来
call add took 0:00:00.000009
Out[6]: 8

函数作为返回值:通常是用在闭包的时候,需要封装一些变量

函数作为参数:通常用于大多数逻辑固定,少部分逻辑不固定的场景

函数作为参数,返回值也是函数:通常用于作为参数函数执行前后需要一些额外的操作

装饰器

参数是一个函数,返回值是一个函数的函数,就可以作为装饰器

只有使用 @ 形式的函数才是一个装饰器

@logger
def sleeps(x):
    time.sleep(x)

使用 functools.wraps 函数来完善使用装饰器带来的副作用

当使用装饰器后,被装饰的函数的 __name__和__doc__方法都是变成装饰器中的二级函数

print('__name__ is {}, __doc__ is {}'.format(sleeps.__name__,sleeps.__doc__))
__name__ is 'wrap', __doc__ is 'None'

在有些时候是需要调用到函数本身的名称的, 因此使用 functools.wraps 将被装饰的函数变回原来的样子

import datetime
import time
import functools
def logger(fn):
    @functools.wraps(fn)   在这里重新装饰下装饰器的二级函数
    def wrap(*args, **kwargs):
        start = datetime.datetime.now()
        ret = fn(*args, **kwargs)
        end = datetime.datetime.now()
        print('call {} took {}'.format(fn.__name__,end-start))
        return ret
    return wrap

@logger
def sleeps(x):
    time.sleep(x)

print('__name__ is {}, __doc__ is {}'.format(sleeps.__name__,sleeps.__doc__))   获取的就是被装饰函数本身的信息

functools.wraps 装饰器的原型

def wraps(src):
    def _copy(dst):
        dst.__name__ = src.__name__
        dst.__doc__ = src.__doc__
        return dst
    return _copy

带参数的装饰器

带参数的装饰器: 一个函数,返回一个不带参数的装饰器,那么这个函数就是一个带参数的装饰器

通俗点来说,就是在装饰器外面包裹一个函数,也可以说是,一个带参数的函数封装了一个装饰器,就变成了一个带参数的装饰器

装饰器最多是两层嵌套

import datetime
import functools
import time

def logger(s):
    def _logger(fn):
        @functools.wraps(fn)
        def wrap(*args, **kwargs):
            start = datetime.datetime.now()
            ret = fn(*args, **kwargs)
            end = datetime.datetime.now()
            if (end-start).total_seconds() > s:
                print('{} call {} took'.format(fn.__name__, end-start))
            return ret
        return wrap
    return _logger

@logger(2)
def sleep(x):
    time.sleep(x)

sleep(1)   不会返回任何结果
sleep(3)   返回结果集

lambda和装饰器结合使用,直接修改上面的装饰器

def logger(s, p=lambda name,t: print('{} call {} took'.format(name, t))):
    def _logger(fn):
        @functools.wraps(fn)
        def wrap(*args, **kwargs):
            start = datetime.datetime.now()
            ret = fn(*args, **kwargs)
            end = datetime.datetime.now()
            if (end-start).total_seconds() > s:
                p(fn.__name__, end-start)   调用lambda函数
            return ret
        return wrap
    return _logger

操作函数的方法

给自定义的对象添加帮助信息

def fn():
    ''' this is fn '''

使用 doc 也是看帮助信息

fn.__doc__

使用 name 获取对象名称

fn.__name__

使用 dir 获取对象所有的方法

dir(fn)

我们要如何帮助您?

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注