Python学习笔记-Day60-装饰器

今日内容:

一、函数名的应用

二、闭包

三 、装饰器

四、装饰器进阶

一、函数名的应用

1、函数名可以当作变量赋值给另一个变量

2、函数名可以作为参数

3、函数名可以作为返回值

4、函数名可以作为容器中的元素

二、闭包

1、闭包的灵魂三问

① 

def func1():
    name = '张三'
    
    def func2(arg):
        print(arg)
    func2(name)

func1()

② 

def func1():
    name = '张三'
    
    def func2():
        print(name)  # 能够访问外层作用域的变量
    func2()
func1()

③ 

def func1(name):  # 函数的形参相当于在函数内部定义了一个变量
    
    def func2():
        print(name)   # 能够访问到外层作用域的变量
    func2()
    
func1('张三')

2、什么是闭包?

闭包就是内层函数调用外层函数的(非全局的)变量,这个内层函数就是一个闭包;在Python中,可以用__closure__来检测一个函数是否是闭包

3、闭包的作用

如果Python中的一个函数,引用了外层函数的变量,那么这个变量不会随着外层函数的结束而被销毁,而是会被保留在内存中。

也就是说,闭包可以保留变量的使用

4、如何在函数的外部调用内层函数?

将内层函数名作为外层函数的返回值

def wrapper():
    name='张三'
   
   def inner(): print(name) return inner#把内部函数当作返回值返回
ret=wrapper()#把返回值赋值给变量ret,ret=inner ret()#就可以调用内部函数,相当于inner()

三、装饰器的定义

1、装饰器的来历

软件设计原则:开闭原则(开放封闭原则)

对扩展代码的功能是开放的,但是对修改源代码是封闭的,这样的软件设计能更好的开发和维护我们的代码

2、装饰器的作用

在不改变函数的结构和调用方式的基础上,动态的给函数添加功能

例如:我们自己写的给不改变函数的调用方式,给create_people添加功能

def create_people():  # 原来的函数
    print('女娲造人啦!!')

def a(func):  # 加了功能的函数
    def b():
        print('加水')
        func()
    return b

create_people = a(create_people)   # 给create_people重新赋值,相当于create_people = b
create_people()  # 相当于执行了b函数
# 最后的结果是:
# 加水
# 女娲造人啦!!

3、装饰器语法糖

利用装饰器,可以简化上面的代码

def wrapper(func):   # 装饰器
    def inner():
        print('加水')
        func()
    return inner

@wrapper
def create_people():  # 原来的函数
    print('女娲造人啦!!')

create_people()  # 装饰之后的函数
# 最终的结果还是:
# 加水
# 女娲造人啦!!

@wrapper的作用是:

  ① 将create_people作为参数传给wrapper函数

  ② 将inner函数赋值给create_people函数

四、装饰器的进阶

1、装饰带返回值的函数

就是在装饰器的inner函数中获取原函数的返回值,并在inner函数最后将其作为返回值返回

def wrapper(func):
    def inner():
        print('这是新功能')
        r = func()   # 获取func原来的返回值
        return r  # 返回func原来的返回值
    return inner

@wrapper
def f1():
    return '嘿嘿嘿'

ret = f1()  # 执行了新功能
print(ret)  # 返回了原来的f1函数的返回值"嘿嘿嘿"

2、装饰带参数的函数

让装饰器函数中的inner函数带上和被装饰函数一样的参数,通用的写法是inner(*args,**kwargs)

def wrapper(func):
    def inner(x,y):
        print('这是新功能')
        func(x,y)
    return inner

@wrapper()
def f1(x,y):
    print("{}+{}={}".format(x,y,x+y))
#
f1(10,20)  # 这是新功能 10+20=30

3、带参数的装饰器

有时候,我们需要为我们的装饰器传递参数

我们使用装饰器是直接@wrapper,没有可以加参数的地方;

此时我们可以在原来的装饰器的外层再定义一个函数用来传参,并在最后返回原来的装饰器函数名。

def d(a=None):  # d是新添加的最外层函数,为我们原来的装饰器传递参数,a就是要传递的参数
    def wrapper(func):  # wrapper 就是我们原来的装饰器函数,func是被装饰的函数
        def inner(*args,**kwargs):  #args和kwargs是被装饰函数的参数
            if a:
                print('欢迎来到{}页面'.format(a))
            else:
                print('欢迎来到首页')
            func(*args,**kwargs)  # 调用被装饰的函数,接收参数
        return inner
    return wrapper

@d()  # 不给装饰器传参数,使用默认的None参数
def index(name):
    print('hello{}'.format(name))

@d('电影')  # 给装饰器函数传参数
def movie(name):
    print('hello{}'.format(name))

if __name__ == '__main__':
    index('张三')  # 欢迎来到首页 hello张三
    movie('张三')  # 欢迎来到电影页面 hello张三

4、多个装饰器装饰同一个函数

同一个函数可以被多个装饰器装饰,此时需要注意装饰器的装饰顺序和函数的执行顺序

def wrapper1(func):
    print('d1')
    def inner1():
        print('inner1')
        return '<i>{}</i>'.format(func())
    return inner1

def wrapper2(func):
    print('d2')
    def inner2():
        print('inner2')
        return '<b>{}</b>'.format(func())
    return inner2

@wrapper1
@wrapper2
def f1():
    return "Hello Andy"

ret = f1()  # 结果是 inner1 inner2
print(ret)  # 结果是 <i><b>Hello Andy</b></i>

装饰顺序:

① f1先被wrapper2装饰,所以先打印”d2“,此时 f1 = inner2

② 然后被wrapper2装饰,打印”d1“,此时 f1 = wrapper1(inner2) = inner1

函数执行的时候:

① 函数刚开始执行的时候,f1 = inner1,所以先执行inner1函数,所以先打印inner1,此时返回值是<i>{}</i>.format(inner2())

② 执行inner2函数,打印inner返回值2,此时返回值是<b>{}</b>.format(f1())

③ 执行f1函数,返回值是”Hello Andy“,再逐一将结果返回,最终的返回值是<i><b>Hello Andy</b></i>

原文地址:https://www.cnblogs.com/tian-tian/p/9886392.html