python-装饰器

一、函数基础

1.函数基础

定义函数,未调用,函数内部代码块不会被执行

函数名即不带括号,代指函数

函数名+(),即f1() ,表示执行f1()函数

'''例1'''
def foo():
    print('foo')
foo    #表示是函数,函数名指函数所在内存中的位置,如果带后面括号表示执行函数
foo()   #表示执行函数
'''例2:'''
def foo():
    print('foo')
foo = lambda x=1: x+1
foo()  #执行下面的lambda表达式,而不再是原来的foo函数,因为函数foo被重新定义

 以上code说明:从成晨博客里看到的。引用一下。

1.在def 函数时,以顺序执行,如果相同的函数名,会被最后函数覆盖以前的

2.如果直接func名而没有后面的圆括号的话,只指向函数在内存中的位置

3.lambda表达式会自动return结果,而def需要定义return值

2.把函数作为参数传递

def f1():
    print('123')
def f2(xx):
    xx()

f2(f1)

输出结果:
12

将函数名称f1作为参数,传递给f2()函数,此时f1== xx  ,xx() == f1,  函数f2()内执行xx()  实际上是执行函数f1()

 

二、装饰器

1.

def outer(func):
   
    return "132"

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

@ + 函数名

功能:

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

2.将outer函数的返回值,重新赋值给f1,  相当于把函数f1重写了,即  f1 = "123" 

 下面证明一下上面的结论:

def outer(func):
    def inner():
        print('before')
    return inner

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

f1()

输出结果:
before

函数f1() 被装饰器装饰之后,如果执行f1() 那么不再是执行print("F1")了,而是执行的是inner()函数。因为装饰器outer返回了inner,即把inner整个函数返回给了f1,此时也就是把f1函数重写为inner函数了,也就是把原来的f1()函数用inner()函数给覆盖掉了。。所以执行的结果是 :  before   而不是 F1

 

2.深入理解装饰器

def outer(func):
    def inner():
        print('before')
        func()
        print('after')
    return inner

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

f1()
输出结果:
before
F1
after

@ + 函数名

功能:

1.自动自行outer函数并且将其下面的函数名f1当作参数传递,即f1 = func  如果执行  func()  那么执行的就是f1()函数,即print("F1")

2.将outer函数的返回值,重新赋值给f1,即把函数f1重写了。即 f1函数变成了inner函数。

3.一个函数f1一旦被装饰器装饰之后,那么这个函数f1将会被重新赋值,赋值为装饰器的内层函数,即:,如果执行f1()函数的时候,实际上是执行函数,又因为inner()函数里面的 func ==原来的 f1 即func() 为print("F1"),所以执行func()函数,就是执行原来的f1()函数,即执行print("F1")

 

三、装饰器python解释的步骤:

装饰器执行之前首先是pyhton解释器解释代码:

步骤如下:

解释器解释的步骤:
第一步:遇到了def outer()  不会执行outer()函数的内容,此时只是把outer()函数的整体代码放到内存中;
 
第二步:会把  @outer和函数f1()作为一个整体作为第二步,即执行:
1.自动自行outer函数并且将其下面的函数名f1当作参数传递,即f1 = func  如果执行  func()  那么执行的就是f1()函数,即print("F1")

 

  第三步:执行到def inner(),解释遇到def 表示遇到了函数,此时不会执行函数里的代码,只是把函数整体代码放到内存中
第四步:直接执行return inner了,将outer函数的返回值,重新赋值给f1,即把函数f1重写了。即 f1函数变成了inner函数。
经过以上步骤后,得到的结果就是 f1函数 成为了 即成为了inner函数。如果没有调用f1函数的时候,相当于在内存中定义了个函数f1,函数f1等于inner函数,一直在内存中存放着,直到别人调用的时候才会执行。
 

四、装饰器执行步骤

调用f1函数:
开始有人调用f1函数了,即执行f1()了
当别人调用f1函数的时候,即f1(),实际上就是执行inner函数,因为python解释器执行的结果就是把inner重新赋值给了f1函数,所以就是执行inner函数内容。
 
执行步骤:
第一步:执行 print('before') 即输出:  before
第二步:执行 func() 即执行老的f1()函数,即执行print("F1"),也就是第三步 ,即输出:  F1
第四步:执行print("after"),即输出: after

 五、装饰器返回值:

获取原函数的返回值
def outer(func):
    def inner():
        print('before')
        r = func()
        print('after')
        return r
    return inner

@outer
def f1():
    print("F1")
    return "好好学习,天天向上"


ret = f1()
print("返回值:" ,ret)

输出结果:
before
F1
after
返回值: 好好学习,天天向上

 

六、带参数的装饰器:

def outer(func):
    def inner(*args,**kwargs):
        print('before')
        r = func(*args,**kwargs)
        print('after')
        return r
    return inner

@outer
def f1(arg):
    print("f1函数的参数:%s" %arg)
    return "好好学习"

@outer
def f2(a1,a2):
    print("f2函数的第一个参数:%s, 第二个参数:%s"  %(a1,a2))
    return "天天向上"
ret1 = f1("F1")
print("f1函数返回值:" ,ret1)
ret2 = f2("a","b")
print("f1函数返回值:" ,ret2)

输出结果:
before
f1函数的参数:F1
after
f1函数返回值: 好好学习
before
f2函数的第一个参数:a, 第二个参数:b
after
f1函数返回值: 天天向上

 

七、装饰器练习:

#!/usr/bin/env  python
# -*- coding:utf-8 -*-
LOGIN_USER = {"is_login":False}

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

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

def login(user,pwd):
    if user == "alex" and pwd == "123":
        LOGIN_USER['is_login'] = True
        LOGIN_USER['current_user'] = user
        manager()

def main():
    while True:
        inp = input("1,后台管理:\n2,登录:")
        if inp == '1':
            manager()
        elif inp == '2':
            username = input("请输入用户名:")
            pwd = input("请输入密码:")
            login(username,pwd)

main()

输出结果:
1,后台管理:
2,登录:1
请登录
1,后台管理:
2,登录:2
请输入用户名:alex
请输入密码:123
欢迎alex登录
1,后台管理:
2,登录:

 八、多层装饰器:

 首先我们看一个例子:模拟登录,普通用户权限,管理员权限

 

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Auther: pangguoping

USER_INFO = {}

def check_login(func):
    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):
    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('无权限查看')
        else:
            print('请登录')
    return inner

@check_login
@check_admin
def index():
    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()
        else:
            pass

main()
View Code

 

输入结果:

从结果中可以看出,已经满足需求

这个例子有明显缺点:

装饰器的功能应该单一,不应该混合

check_admin 即判断登录,又判断权限。

check_login是判断登录。
所以功能重复了。应该让check_admin 只判断权限,check_login只判断登录;这样规划比较合理
下面我们优化一下:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Auther: pangguoping

USER_INFO = {}

def check_login(func):
    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):
    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():
    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()
        else:
            pass

main()
View Code

 

 

优化后的代码我们使用了多层装饰器

九、多层装饰器 python解释器解释过程:

解释过程:从下往上

1.首先把check_login函数放入内存;
2.把check_admin函数放入内存;
3.把装饰器check_admin和函数index 一块解释,即
 ,此时index函数成为了装饰器check_admin里的inner函数,即:,相当于把装饰器里的inner函数放到了 index函数的位置,即,我们假如index函数变成了 check_admin_inner_index函数
 
 
 
4.从第3步我们可以知道,通过解释 @装饰器check_admin和函数index函数 ,而且@装饰器check_admin里面的inner函数替代了Index函数,我们假如此时的index函数命名check_admin_inner_index,所以下面的解释步骤为:
即,
 
 现在是解释@check_login  和 def check_admin_inner_index函数  
装饰器check_login的func参数指的就是 check_admin_inner_index函数,然后装饰器check_login里的inner函数会覆盖 check_admin_inner_index函数,我们假如覆盖后的函数为check_login_inner_index,此时的index函数 就是check_login_inner_index即
 
这是整个解释的过程。
 

十、多层装饰器执行过程:

 执行过程:从上往下
1.调用index函数,即index()
2.执行 装饰器check_login里的inner函数(我们假如if里面的条件都满足),即
 ,
第一步执行if判断(我们假如条件满足),第二步执行ret = func(*args,**kwargs) ,这里的func等价于装饰器check_admin里面的inner函数,所以第三步:执行装饰器check_admin里面的inner函数,
,这里的func函数等价于最原始的的index函数,即
 所以第四步:执行func函数,第五步;执行原始的index函数,
第六步:执行装饰器check_login里函数inner里的return ret ,把这个值返回给调用他的函数,即返回给装饰器check_admin里inner函数里 ret = func(*args,**kwargs),虽然返回值为None
第七步:执行装饰器check_admin里inner函数里 的return ret 即,虽然返回值为None
 
整个过程:

 

 

 

posted @ 2016-06-03 12:36  unixfbi.com  阅读(241)  评论(0编辑  收藏  举报