【Python实践】_装饰器

真正的稳定,是自己不断成长,不断寻找新的空间。与其要稳定,不如开始拥抱这个变化的时代,让自己准备好。


python实践

【目录】Python实践

【写在前面】:
曾经在面试的时候问过很多人“什么是装饰器?”,但是都没有得到一个很简洁明了的答案,希望这篇文章能使你对装饰器有个比较清楚、简洁的认识。
【材料】:
参考材料 《Python进阶》

【Step1】:什么是装饰器?
从字面意思来理解,装饰+器,装饰我们都知道,是在原有基础上增加某些功能,达到一定的效果,python中的装饰器也是在原来函数(类)上增加一些特殊的功能,但是并不是直接去修改原来的函数(类),而是新写一个函数,这个函数的功能就是在对原函数(类)二次处理(并非重构)
简单举例如下:

# -*- coding: utf-8 -*-
def greet(fun):
    def pre():
        print("I'm pre")
        t = fun()
        return t
    return pre


@greet
def test():
    return "I'm test"


if __name__ == '__main__':
    print(test())

运行结果如下:

I'm pre
I'm test

上面的greet函数就是一个装饰器,其作用就是对test函数做装饰,在待装饰的函数前面使用@符号来调用装饰函数。
【Step2】:对Step1的解释
1、对函数的认识,通常def test()被叫做定义一个test函数,这其中test是函数名,当使用test()时表示在调用该函数,可以拿到return值,当使用test时并不会调用,只是将函数作为一个变量,可以赋值给其他变量,比如a,这样a变量就有了test函数的功能,而且两个函数名都指向该函数的内存地址(0x012B5618)。这里我们了解到原来函数不是只能直接被调用,函数名还能够作为参数使用,这一点很重要。

def test():
    return "I'm test"


if __name__ == '__main__':
    print(test())
    print(test)
    a = test
    print(a)
    print(a())
    del test
    print(a())

运行结果如下:

I'm test
<function test at 0x00B25618>
<function test at 0x00B25618>
I'm test
I'm test

2、函数中定义函数
函数out中包含子函数inner,调用out函数时,会return出该函数的输出,即“here is out...”,在out函数内部有print(inner())来打印inner函数的return结果,即“here is inner...”(如果没有这一句,是不会调用inner函数的),而由于inner函数是在out函数内部的,所以不能被单独调用。
举例如下:

def out():
    print("here is out...")

    def inner():
        return "here is inner..."
    print(inner())


out()
inner()

运行结果如下:

here is out...
here is inner...
  File "D:/auto_case/DailyWork/test.py", line 10, in <module>
    inner()
NameError: name 'inner' is not defined

3、从函数中返回函数
现实中很少会在函数中去执行一个函数,更多的时候是希望将内部函数作为外层函数的return值,所以我们可以这样改造,达到返回内层函数的效果
(1).out函数,这个就是out的一个函数,return a
(2).out1函数,内层inner1函数,在out1函数中使用inner1()调用inner1,返回inner1函数return的值;
(3).out2函数,内层inner2函数,在out2函数中使用inner2将函数赋值给了变量c,返回函数c(注意不是函数c被调用的值);
(4).out3函数是对out2函数的简写,功能一样;
(5).在print中,重点是out3()是一个函数,所以要得到函数返回值,就需要在其后面加上小括号,达到调用函数的效果,即out3()()。
举例如下:

# -*- coding: utf-8 -*-
def out():
    a = "test"
    return a


def out1():
    def inner1():
        return "here is inner..."
    b = inner1()
    return b


def out2():
    def inner2():
        return "here is inner2..."
    c = inner2
    return c


def out3():
    def inner3():
        return "here is inner3..."
    return inner3


print(out())
print(out1())
print(out2())
d = out2()
print(d())
print(out3())
print(out3()())

运行结果如下:

test
here is inner...
<function out2.<locals>.inner2 at 0x01A3D8E8>
here is inner2...
<function out3.<locals>.inner3 at 0x01A3D618>
here is inner3...

4、将函数名作为参数传递给其他函数
函数名可以代表函数,也可以赋值,所以能够作为参数专递到函数中去,在函数中只要在参数后面加上小括号,就拥有了该函数的功能。我在下面的函数中加了步骤输出,读者可以比较清楚的知道函数是如何调用的。
举例如下:

# -*- coding: utf-8 -*-
def test():
    print("step 4")
    return "I'm test"


def greet(fun):
    print("step 1")
    
    def pre():
        print("step 2")
        print("I'm pre")
        t = fun()
        return t
    print("step 3")
    return pre


print(greet(test))
print(greet(test)())

运行结果如下:

C:\Users\54788\AppData\Local\Programs\Python\Python35-32\python3.exe D:/auto_case/DailyWork/test.py
step 1
step 3
<function greet.<locals>.pre at 0x01C0D588>
~~~~~~虚拟分割线~~~~~~~
step 1
step 3
step 2
I'm pre
step 4
I'm test

Process finished with exit code 0

5、装饰器标识符“@”,使用@装饰器名在待装饰函数前,即可完成不修改待装饰函数的情况下,修改函数的功能,注意装饰器必须在被装饰函数前定义。
【Step3】:应用场景举例
鉴权在打开页面和登录中的使用

# -*- coding: utf-8 -*-
def auth(fun):
    def base_auth(*param):
        if param[0] == '123%$&*sdhjd':
            return fun(*param)
        else:
            return no_auth()
    return base_auth


def no_auth():
    return "you have no auth"


@auth
def open_page(param):
    return "begin to open page..." + str(param)


@auth
def login(param1,param2):
    return "begin to login..." + str(param2)


print(open_page('123%$&*sdhjd'))
print(open_page('123%$&*'))
print(login('123%$&*sdhjd', 'test'))
print(login('123%$&*', 'test'))

推荐阅读更多精彩内容