猴砸

装饰器

装饰器流程剖析

一、装饰器概要

1、需求

基础平台--底层代码的开发实现

不同业务线--底层业务的实现,调用底层代码

底层部门100个函数用来支撑85个业务线

要求:在所有的功能里面加个写日志的功能

1.1、所有业务线的人改

1.2、底层代码这边进行修改(☑️)

方法一、在底层代码s2文件里面直接定义一个函数,专门用来修改这个日志的

s2文件

#!/user/bin/env python
# -*- coding: utf-8 -*-
#100个业务线
def outer():
    print('log')
def f1():
    outer()
    print('F1')

def f2():
    outer()
    print('F2')


def f100():
    outer()
    print('F100')

b1(业务线1)文件

#!/user/bin/env python
# -*- coding: utf-8 -*-

import s2

s2.f1()
s2.f2()

#打印结果
'''
log
F1
log
F2
'''

方法二、使用装饰器

s2文件

#!/user/bin/env python
# -*- coding: utf-8 -*-
#100个业务线
def outer(func):
    def inner():
        print('log') #在执行函数前打印log
        ret = func()
        print('after')  #在执行函数后打印after
        return ret
    return inner

@outer
def f1():
    print('F1')

@outer
def f2():
    print('F2')

@outer
def f100():
    print('F100')

 

 

b1文件

#!/user/bin/env python
# -*- coding: utf-8 -*-

import s2

s2.f1()
s2.f2()

#打印结果
'''
log
F1
after
log
F2
after
'''

 

 

方法一,修改了每一个函数里面内部的功能(加了outer()),在软件开发的时候,应该遵循开放封闭原则

方法二、开放封闭原则:不修改函数内部代码,在函数外部额外再添加一个功能

2、概念

开放封闭原则:对于类、函数的内部对于所有人来说是封闭的,改功能的时候,在函数的外部是开放的,所以只能在函数的外部进行修改

在不改变调用函数的基础上,在执行函数前或者执行函数后添加一个功能

3、装饰器内部结构原理解析

f2中的xxx参数为f1函数的整体,如下代码所示

#!/user/bin/env python
# -*- coding: utf-8 -*-
def f1():
    print(123)
def f1():
    print(456)

f1()  #执行的是456

def f1(): print('123') def f2(xxx): xxx() f2(f1) #f1是函数f1的整体 与f1()不同 #对于函数来说,函数的整体是可以当作参数来进行传递的

二、装饰器流程剖析

1、函数执行过程剖析

  一个函数只是定义的时候,解释器去执行代码的时候是不会执行的

1.1、@符号

    @+函数名 并且放在某个函数的上面

  1.2、功能

    自动执行outer函数并且将其下面的函数名f1当作参数传递;

    将outer函数的返回值,重新赋值给f1

    详见下面代码👇:

def outer(func):
    return '111'

@outer
def f1():
    print('F1')

print(f1)

#打印结果:111 ''' 等于执行了 f1 = '111', 不执行print('F1') '''

 

2、将返回值换成函数

  2.1、将上面的return '111'替换成def inner(): print('before'),等同于将f1赋值成了def inner(): print('before') 这个函数,而不是print('F1')

    所以最终打印的结果就是before

#!/user/bin/env python
# -*- coding: utf-8 -*-
#100个业务线
def outer(func):
    def inner():
        print('before')
    return inner

@outer
def f1():
    print('F1')

print(f1)

#打印结果:before

 

2.2、在执行print('before')的同时,也进行执行print('F1')

  如下代码中的func参数指的是老的f1函数(def f1(): print('F1')

func():执行def f1(): print('F1')

 

#!/user/bin/env python
# -*- coding: utf-8 -*-
#100个业务线
def outer(func):
    def inner():
        print('before') #输出before
        func()   #输出F1
    return inner

@outer
def f1():
    print('F1')


#打印结果:
'''
before
F1
'''

2.3、在执行函数之后print一个after

#!/user/bin/env python
# -*- coding: utf-8 -*-
#100个业务线
def outer(func):
    def inner():
        print('before') #输出before
        func()   #输出F1
        print('after')
    return inner

@outer
def f1():
    print('F1')


#打印结果:
'''
before
F1
after
'''

3、总结

3.1、定义函数未调用,函数内部不执行

3.2、函数名:代指整个函数(不加括号)

3.3、执行顺序:4-11、12、13-5-9-6-7-13-8

 1 #!/user/bin/env python
 2 # -*- coding: utf-8 -*-
 3 #100个业务线
 4 def outer(func):
 5     def inner():
 6         print('before') #输出before
 7         func()   #输出F1
 8         print('after')
 9     return inner  #函数整体,如果是inner()的时候,因为inner函数没有返回,所以返回的是None,系统会处于瘫痪状态
10 
11 @outer
12 def f1():
13     print('F1')
14 
15 
16 #打印结果:
17 '''
18 before
19 F1
20 after
21 '''

 

三、装饰器流程剖析之返回值

1、应用场景

1.1、在不改变原函数内容的前提下,再之前或者之后统一的做一些操作

2、返回值

2.1、如下代码中,f1函数(原函数)没有返回值,返回值是一个None

s2文件

#!/user/bin/env python
# -*- coding: utf-8 -*-
#100个业务线
def outer(func):
    def inner():
        print('before') #输出before
        func()   #输出F1
        print('after')
    return inner()

@outer
def f1():
    print('F1')

 

b1文件

#!/user/bin/env python
# -*- coding: utf-8 -*-

import s2

ret = s2.f1()
print('返回值',ret)


#打印结果
'''
before
F1
after
返回值 None
'''

 

2.2、原函数(f1())有返回值

   return r需要在print('after')的后面,因为不然函数执行过程中遇到了return就不会再执行下面的函数了

   无法实现,在执行原函数之后执行后面的操作

s1文件

#!/user/bin/env python
# -*- coding: utf-8 -*-
#100个业务线
def outer(func):
    def inner():
        print('before') #输出before
        r = func()   #输出F1,并且将f1中返回值返回给了r
        print('after')
        return r  #将返回的“有返回值”这个原函数的返回值返回
    return inner

@outer
def f1():
    print('F1')
    return "有返回值"

 

b1文件

#!/user/bin/env python
# -*- coding: utf-8 -*-

import s2

ret = s2.f1()
print('返回值',ret)


#打印结果
'''
before
F1
after
返回值 有返回值
'''

四、装饰器流程剖析之参数

1、应用装饰器之前,原函数有参数

 s2文件

def f1(arg):
    print(arg)
    return "有返回值" 

b1文件

#!/user/bin/env python
# -*- coding: utf-8 -*-

import s2

ret = s2.f1('原函数有参数的参数')
print('返回值',ret)


#打印结果
'''
原函数有参数的参数
返回值 有返回值
'''

2、应用了装饰器之后的,原函数只有一个原函数f1(arg)

2.1、错误实例

解析:因为应用了装饰器之后,f1中的函数执行的是inner()函数,但是inner()没有参数,所以报错

s2文件

#!/user/bin/env python
# -*- coding: utf-8 -*-
#100个业务线

def outer(func):
    def inner():
        print('before') #输出before
        r = func()   #输出F1
        print('after')
        return r
    return inner

@outer
def f1(arg):
    print(arg)
    return "有返回值" 

b2文件

#!/user/bin/env python
# -*- coding: utf-8 -*-

import s2

ret = s2.f1('原函数有参数的参数')
print('返回值',ret)


#打印结果
'''
Traceback (most recent call last):
  File "/Users/liyueting/sday13/b1.py", line 6, in <module>
    ret = s2.f1('原函数有参数的参数')
TypeError: inner() takes 0 positional arguments but 1 was given
'''

2.2、正确实例

解析:在inner()与func()函数都需要加上参数,并且两处加的参数需要是相同的,不然会报错

s2文件

#!/user/bin/env python
# -*- coding: utf-8 -*-
#100个业务线

def outer(func):
    def inner(a):
        print('before') #输出before
        r = func(a)   #输出F1
        print('after')
        return r
    return inner

@outer
def f1(arg):
    print(arg)
    return "有返回值"

b1文件

#!/user/bin/env python
# -*- coding: utf-8 -*-

import s2

ret = s2.f1('原函数有参数的参数')
print('返回值',ret)


#打印结果
'''
before
原函数有参数的参数
after
返回值 有返回值
'''

3、f1有一个参数、f2有两个参数

3.1、错误实例

s2文件

#!/user/bin/env python
# -*- coding: utf-8 -*-
#100个业务线

def outer(func):
    def inner(a):
        print('before') #输出before
        r = func(a)   #输出F1
        print('after')
        return r
    return inner

@outer
def f1(arg):
    print(arg)
    return "有返回值"

@outer
def f2(arg1,arg2):
    print("F2")

b1文件

#!/user/bin/env python
# -*- coding: utf-8 -*-

import s2

ret = s2.f1('原函数有参数的参数')
print('返回值',ret)

s2.f2(11,22)




#打印结果
'''
before
原函数有参数的参数
after
返回值 有返回值
Traceback (most recent call last):
  File "/Users/liyueting/sday13/b1.py", line 9, in <module>
    s2.f2(11,22)
TypeError: inner() takes 1 positional argument but 2 were given
''' 

3.2、正确实例

s2文件

#!/user/bin/env python
# -*- coding: utf-8 -*-
#100个业务线

def outer(func):
    def inner(*args,**kwargs):  #使用万能参数,支持传无数个参数
        print('before')
        r = func(*args,**kwargs)   #使用万能参数,支持传无数个参数
        print('after')
        return r
    return inner

@outer
def f1(arg):
    print(arg)
    return "有返回值"

@outer
def f2(arg1,arg2):
    print("F2") 

b1文件

#!/user/bin/env python
# -*- coding: utf-8 -*-

import s2

ret = s2.f1('原函数有参数的参数')
print('返回值',ret)

s2.f2(11,22)




#打印结果
'''
before
原函数有参数的参数
after
返回值 有返回值
before
F2
after
'''

五、用户管理程序实例

1、需求

 利用装饰器做一个登录的功能,具体功能为:登录一个网站的时候,部分功能进行权限判断或者是账号不能查看

2、相关代码

2.1、不采用装饰器

LOGIN_USER = {'is_login':False}

def order():
    if LOGIN_USER['is_login']:
        print('欢迎%s登录' %LOGIN_USER['current_user'])
    else:
        print('请登录')

def changepwd():
    if LOGIN_USER['is_login']:
        print('欢迎%s登录' %LOGIN_USER['current_user'])
    else:
        print('请登录')

def manager():
    if LOGIN_USER['is_login']:
        print('欢迎%s登录' %LOGIN_USER['current_user'])
    else:
        print('请登录')


def login(user,pwd):
    if user == 'zhangsan' and pwd =='123456':
        LOGIN_USER['is_login'] = True
        LOGIN_USER['current_user'] = user
        manager()
def main():
    while True:
        inp = input('1,后台登录;2,登录')
        if inp == '1':
            manager()
        elif inp == '2':
            username = input('请输入用户名')
            pwd = input("请输入密码")
            login(username,pwd)
main()

2.2、利用装饰器

LOGIN_USER = {'is_login':False}

def outer(func):
    def inner(*kags,**kwargs):
        if LOGIN_USER['is_login']:
            r = func()
            return r
        else:
            print('请登录')
    return inner

@outer
def order():
    print('欢迎%s登录' %LOGIN_USER['current_user'])
@outer
def changepwd():
    print('欢迎%s登录' %LOGIN_USER['current_user'])
@outer
def manager():
    print('欢迎%s登录' %LOGIN_USER['current_user'])

def login(user,pwd):
    if user == 'zhangsan' and pwd =='123456':
        LOGIN_USER['is_login'] = True
        LOGIN_USER['current_user'] = user
        manager()
def main():
    while True:
        inp = input('1,后台登录;2,登录')
        if inp == '1':
            manager()
        elif inp == '2':
            username = input('请输入用户名')
            pwd = input("请输入密码")
            login(username,pwd)
main()

双层及多层装饰器

一、双层装饰器

1、dict的get功能

USER_INFO = {}
USER_INFO.get('is_login',None)  #如果字典中没有相对应的value的时候,用get方法的时候需要将value默认为None

 

2、登录身份区分

 

普通用户:只要登录就可以查看

超级管理员:登录+相对应的权限

3、利用多个重复的装饰器完成

 

USER_INFO = {}

def check_login(func):
    """
    普通用户的装饰器
    :param func:
    :return:
    """
    def inner(*args,**kwargs):
        if USER_INFO.get('is_login',None):
            ret = func(*args,**kwargs)
            return ret
        else:
            print('请登录')
    return inner

def check_admin(func):
    """
    超级管理员的装饰器
    :param func:
    :return:
    """
    def inner(*args,**kwargs):
        if USER_INFO.get('is_login',None):
            if USER_INFO.get('user_type',None)==2:
                ret = func(*args,**kwargs)
                return ret
            else:
                print('无权限查看')
    return inner

@check_admin
def index():   #超级管理员
    """

    :return:
    """
    print('Index')

@check_login
def home():#普通管理员
    """

    :return:
    """
    print('home')

def login():
    user = input('请输入用户名:')
    if user == 'admin':
        USER_INFO['is_login'] = True
        USER_INFO['user_type'] = 2
    else:
        USER_INFO['is_login'] = True
        USER_INFO['user_type'] = 1

def main():
    while True:
        inp = input('1、登录;2、查看信息;3、超级管理员管理 \n >>>')
        if inp == '1':
            login()
        elif inp == '2':
            home()
        elif inp == '3':
            index()

main()

#打印结果
'''
1、登录;2、查看信息;3、超级管理员管理 
 >>>1
请输入用户名:zhangsan
1、登录;2、查看信息;3、超级管理员管理 
 >>>2
home
1、登录;2、查看信息;3、超级管理员管理 
 >>>3
无权限查看
1、登录;2、查看信息;3、超级管理员管理 
 >>>1
请输入用户名:admin
1、登录;2、查看信息;3、超级管理员管理 
 >>>2
home
1、登录;2、查看信息;3、超级管理员管理 
 >>>3
Index
1、登录;2、查看信息;3、超级管理员管理 
 >>>
'''
View Code

 

 

 

4、利用双层装饰器完成

4.1、相关代码

 

USER_INFO = {}

def check_login(func):
    """
    普通用户的装饰器
    :param func:
    :return:
    """
    def inner(*args,**kwargs):
        if USER_INFO.get('is_login',None):
            ret = func(*args,**kwargs)
            return ret
        else:
            print('请登录')
    return inner

def check_admin(func):
    """
    超级管理员的装饰器
    :param func:
    :return:
    """
    def inner(*args,**kwargs):
        if USER_INFO.get('user_type',None)==2:
            ret = func(*args,**kwargs)
            return ret
        else:
            print('无权限查看')
    return inner

@check_login
@check_admin
def index():   #超级管理员
    """

    :return:
    """
    print('Index')

@check_login
def home():#普通管理员
    """

    :return:
    """
    print('home')

def login():
    user = input('请输入用户名:')
    if user == 'admin':
        USER_INFO['is_login'] = True
        USER_INFO['user_type'] = 2
    else:
        USER_INFO['is_login'] = True
        USER_INFO['user_type'] = 1

def main():
    while True:
        inp = input('1、登录;2、查看信息;3、超级管理员管理 \n >>>')
        if inp == '1':
            login()
        elif inp == '2':
            home()
        elif inp == '3':
            index()

main()

#打印结果
'''
1、登录;2、查看信息;3、超级管理员管理 
 >>>3
请登录
1、登录;2、查看信息;3、超级管理员管理 
 >>>1
请输入用户名:admin
1、登录;2、查看信息;3、超级管理员管理 
 >>>3
Index
1、登录;2、查看信息;3、超级管理员管理 
 >>>
'''
View Code

4.2、多个装饰器的执行顺序

10-11-24-25-38-26-12

 1 USER_INFO = {}
 2 
 3 def check_login(func):
 4     """
 5     普通用户的装饰器
 6     :param func:
 7     :return:
 8     """
 9     def inner(*args,**kwargs):
10         if USER_INFO.get('is_login',None):
11             ret = func(*args,**kwargs)
12             return ret
13         else:
14             print('请登录')
15     return inner
16 
17 def check_admin(func):
18     """
19     超级管理员的装饰器
20     :param func:
21     :return:
22     """
23     def inner(*args,**kwargs):
24         if USER_INFO.get('user_type',None)==2:
25             ret = func(*args,**kwargs)
26             return ret
27         else:
28             print('无权限查看')
29     return inner
30 
31 @check_login
32 @check_admin
33 def index():   #超级管理员
34     """
35 
36     :return:
37     """
38     print('Index')
39 index()
  

 4.4、相关执行流程

本质:一个装饰器与一个原函数结合之后 = 新的函数

函数名:index

函数体:详见下图

执行顺序:nindex-index(第一个)

执行顺序:nnindex--nindex-index(第二个)

4.5、整体的执行顺序:

先执行check_login里面的inner函数--再执行check_adim里面的inner的函数--再执行index下面的函数

执行:从上往下执行

@check_login
@check_admin
def index():   #超级管理员
    """

    :return:
    """
    print('Index')

解释:从下往上一步一步编译的

二、多层装饰器

与双层装饰器一样

三、其他装饰器

详见:http://www.cnblogs.com/wupeiqi/articles/4980620.html

 

posted @ 2017-09-12 16:29  猴砸  阅读(155)  评论(0编辑  收藏  举报