装饰器分成三种:

1、无参数的;

2、有参数的;

3、装饰器本身带参数的。

 

装饰器decorator又叫语法糖

定义:本质是函数,器就是函数的意思。装饰其他函数。就是为其他函数添加附加功能。

原则:   1、不能修改被装饰的函数的源代码;

    2、不能修改被装饰的函数的调用方式;

    3、装饰器对被装饰的函数是完全透明,就是被装饰的函数,根本不知道装饰器的存在。

实现装饰器知识储备:

1、函数即“变量”

2、高阶函数

3、嵌套函数

高阶函数+嵌套函数=》装饰器的效果

1、函数即变量的意义:

函数跟变量一样,都是内存放着函数体,而有一个函数名指着这个内存的函数体。

计算机的变量x,y就是一个门牌号,他们都指向数值1,对于计算机的内存回收机制,只要数值1,有任意一个变量指向它,那么这个数值1在内存里就不会被清空回收;

函数跟变量一样,函数的内存回收机制跟变量是一模一样的。只要存在函数名,那么函数体存在与内存里,就不会被清空回收;

但是匿名函数,是没有函数名的,那么匿名函数,在内存里就会立马被清空回收。

装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_函数体

再来看一个实际的例子,下图中bar()函数,函数体是pass,如果你在命令行里直接打函数名bar(就是门牌号),那么返回的是函数体的内存地址。给函数名bar加两个括号(),那么就是到内存去取函数体,那么bar()返回的就是函数体pass

装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_带参数_02 

第1段, 报错的代码:

装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_函数体_03

装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_函数体_04

第2段代码是正确的:

装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_内存地址_05

装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_内存地址_06

第3段代码,把bar的顺序对调了下:

装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_嵌套_07

装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_内存地址_06

第4段错误的代码:调用foo函数的时候,bar变量还没定义呢,肯定出错

装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_嵌套_09

2、高阶函数的定义:

  • 把一个函数名当作实参传给另外一个函数(在不修改被装饰函数源代码的清下为其添加功能);
  • 返回值中包含函数名。(不修改函数的调用方式)

首先我们来说说把一个函数名当作实参传给另外一个函数的例子:

下面的这个例子,test1(bar) ,bar这个时候就是门牌号,也就是函数体的内存地址,把内存地址当作实参传给了test1这个函数,同时相当于func=bar,相当于把bar的函数体内存地址赋值给了func。

所以func也就是bar的门牌号,func()取的是bar的函数体!

装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_函数体_10

装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_嵌套_11

我们现在升级下上面的这个高阶函数,来做下统计bar这个函数,运行需要多少时间。

模块time:

time.sleep(3)等待3秒

time.time()获取当前时间

时间可以直接相减,获得间隔的秒数

装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_函数体_12

装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_函数体_13

装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_嵌套_14

这里的test1就有点类似装饰器了,它的作用就是统计bar函数运行的时间。

这边有一个知识点,高阶函数需要把函数名当作实参传给另外一个函数,所以在上图中,test1(bar)这个写法是正确的,这个时候是把函数的内存地址传给另外一个函数,

但是test1(bar())这个写法就不是高阶函数了,这个是把bar()这个函数的结果传给另外一个函数。这个写法不能说是错误的,但是就不是高阶函数了。

其次我们来说说返回值要求包含函数名的例子:

装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_函数体_15

装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_高阶函数_16

3、嵌套函数的定义:

下面的这种方式是不行了,不可以调用局部变量:

装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_函数体_17

函数的嵌套,就是在一个函数体里,又申明一次函数。请注意这里说的是申明,而不是调用。

装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_函数体_18

我们再来看一个嵌套函数的例子:这个例子的结果是3

装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_内存地址_19

无参数的装饰器

做了这么多的铺垫,我们现在最终来写一个装饰器:

装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_带参数_20

装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_嵌套_21

装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_嵌套_22

最后,这个写法太麻烦装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_内存地址_23

python3做了个定义:你想给哪个函数加装饰器,就在哪个函数前面@装饰器

@装饰器就表示函数1=装饰器(函数1)

装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_嵌套_24

1、最终无参数的装饰器的代码:注意下各个函数什么时候加(),什么时候不加(),很重要的理解!!!

装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_带参数_25

装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_高阶函数_26

2、有参数的装饰器:(但是注意要兼容无参数的情况,不能说兼容了参数,就把没参数的不兼容了)

 装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_内存地址_27

 装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_嵌套_28

装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_高阶函数_29

装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_内存地址_30

3、装饰器本身带参数的情况,以及如果函数带return结果的情况,比如下面的home()就带了return "from home"的结果。需要在装饰器里加return

 其实上面两种情况已经可以满足你90%的装饰器的情况了。

我们模拟网站,一个页面就是一个函数,其中有几个页面,需要登录以后才可以浏览访问。登录这个操作用装饰器来完成。

首页index(),用户的页面home()需要本地验证登录,bbs()需要远程验证登录

装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_带参数_31

装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_嵌套_32

装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_带参数_33

装饰器1、无参数的装饰器 2、有参数的装饰器 3、装饰器本身带参数的以及如果函数带return结果的情况_带参数_34

user,passwd = 'alex','abc123'
def auth(auth_type):
    print("auth func:",auth_type)
    def outer_wrapper(func):
        def wrapper(*args, **kwargs):
            print("wrapper func args:", *args, **kwargs)
            if auth_type == "local":
                username = input("Username:").strip()
                password = input("Password:").strip()
                if user == username and passwd == password:
                    print("\033[32;1mUser has passed authentication\033[0m")
                    res = func(*args, **kwargs)  # from home
                    print("---after authenticaion ")
                    return res
                else:
                    exit("\033[31;1mInvalid username or password\033[0m")
            elif auth_type == "ldap":
                print("搞毛线ldap,不会。。。。")

        return wrapper
    return outer_wrapper

def index():
    print("welcome to index page")
@auth(auth_type="local") # home = wrapper()
def home():
    print("welcome to home  page")
    return "from home"

@auth(auth_type="ldap")
def bbs():
    print("welcome to bbs  page")

index()
print(home()) #wrapper()
bbs()