Day59 Python基础--闭包&装饰器
一,函数知识点回顾
def func(): pass print(func) #函数名对应内存地址 a = func #可以赋值给变量 a() def func2(arg): #可以做参数 arg() func2(func) def func3(): #可以做返回值 def fun4(): pass return func4 lis = [func,func2,func3,func4] #可以做容器对象的元素 for i in lis: i()
二,闭包
定义:如果一个内层函数包含了对外层函数变量的引用,那这个内层函数就是闭包
闭包是一个特别的情况。外部函数发现,自己的临时变量会在将来的内部函数中用到,自己在结束的时候,返回内函数的同时,会把外函数的临时变量送给内函数绑定在一起。所以外函数已经结束了,调用内函数的时候仍然能够使用外函数的临时变量
def wrapper(name): def inner(): print(name) #相当内层函数的空间里保留了name变量的引用并且指向当初传入的实参 return inner ret = wrapper('123') #当函数执行时,Python解释器会开辟一块内存(局部名称空间)来储存这个函数里面的内容,这个时候,Python解释器才关注函数里面变量名和值之间对应的关系,包括内层函数内的内容 del wrapper ret() # 指向inner的内存地址 wrapper('123') #报错:NameError: name 'wrapper' is not defined
#灵魂三问1 def func1(): num = 100 def func2(arg): print(arg) func2(num) #通过传参的方式,向内层函数传递数据 func1() #灵魂三问2 def func1(): num = 100 def func2() print(num) #内层函数的作用域里没有num,会自动往外层函数的作用域去找 func2() func1() #灵魂三问3 def func1(num): def func2(): print(num) #内层函数的作用域路没有num,会自动往外层函数的作用域去找 func2() func1()
#内层函数+其外层函数变量的引用 def func1(name): def func2(): print(name) return func2 ret = func1('alex') ret() #非闭包 def func1(): def func2(): def func3(): def func4(): pass return func4 return func3 return func2 ret1 = func1() #func2 ret2 = ret1() #func3 ret3 = ret2() #func4 ret3()
三,装饰器初识
#装饰器的定义:
既不想直接修改原来的函数,也不想修改函数的调用方式,还要加新功能的时候,可以使用装饰器 def wrapper(func): def inner(*args,**kwargs): print('新功能') func(*args,**kwargs) return inner
#global_func = wrapper(global_func) 内层的inner函数,引用了func,func -指向-> global_func
@wrapper #相当于把被装饰的函数当成参数传给a,然后把返回值再赋值给被装饰的函数名
def global_func(): pass
global_func() #相当于调用了内层函数inner
四,装饰器进阶
1.装饰带参数的函数
# 装饰带参数的函数 def wrapper(func): def inner(*args, **kwargs): # 实际执行函数的参数 print('开始') r = func(*args, **kwargs) print(r) print('结束') return r return inner @wrapper def my_sum(x, y): print('我是my_sum函数') return x + y ret = my_sum(10, 20) # inner(10, 20) print(ret)
2.装饰带返回值的函数
# 装饰带返回值的函数 def wrapper(func): def inner(): print('开始') r = func() # 拿到原来函数的返回值 print('结束') return r return inner @wrapper def f1(): print('我是f1') return 100 ret = f1() #实际上是执行了inner内层函数,如果inner内层没用renturn返回值,则f1也没用返回值
3.带参数的装饰器 *****
def mougehanshu(arg): def wrapper(func): def inner(*args, **kwargs): print('欢迎VIP用户进入{}专栏!'.format(arg)) func(*args, **kwargs) return inner return wrapper # wrapper = mougehanshu() #@mougehans(参数) 相当于 @wrapper @mougehanshu('电影') # 相当于mougehanshu()执行后返回wrapper def movie(): print('这是电影专栏!') @mougehanshu('体育') # @wrapper def sport(): print('这是体育专栏!')
4.多个装饰器装饰同一个函数 *****
def foo1(func): print("d1") def inner1(): print("inner1") return "<i>{}</i>".format(func()) return inner1 def foo2(func): print("d2") def inner2(): print("inner2") return "<b>{}</b>".format(func()) return inner2 @foo1 #f1 = foo1(foo2(f1))=foo1(inner2,打印d2) = inner1 打印'd1' @foo2 #执行foo2(f1) =inner2 打印'd2' def f1(): return "我是f1" ret = f1() print(ret) # f1 = foo2(f1) --> inner2 # f1 = foo1(inner2) --> inner1 ↓↓ #执行流程分析: #f1() == inner1()->打印'inner1'->return "<i>{}</i>".format(inner2()) #inner2() -> 打印'inner2'->return "<b>{}</b>".format(f1()) #f1() -> return "我是f1" ↓↓ print(ret) ---> '<i><b>我是f1</b></i>'
5.装饰器修复技术 ****
# 装饰器修复技术 from functools import wraps # Flask框架的时候用得上 def wrapper(func): @wraps(func) def inner(*args, **kwargs): print('这是新功能') func(*args, **kwargs) return inner @wrapper def f1(arg1, arg2): """ 这里写这个函数是做什么的 :param arg1: 这个参数是什么类型的 :param arg2: 这个参数是什么类型 :return: None """ print('嘿嘿嘿') print(f1.__doc__) print(f1.__name__) #若不修复的话,f1实际是指向inner的内存空间, #f1.__doc__ 相当于 inner.__doc__中的内容 #f1.__name__ 相当于 inner.__name__中的内容
五,装饰器究极进阶
1.类装饰器:用类装饰函数
class D(object): def __init__(self, a=None): self.a = a self.mode = "装饰" def __call__(self, *args, **kwargs): if self.mode == "装饰": self.func = args[0] # 默认第一个参数是被装饰的函数 self.mode = "调用" return self # 当self.mode == "调用"时,执行下面的代码(也就是调用使用类装饰的函数时执行) if self.a: print("欢迎来到{}页面。".format(self.a)) else: print("欢迎来到首页。") self.func(*args, **kwargs) @D() # index = D()(index) 先实例化在传入函数 def index(name): print("Hello {}.".format(name)) @D("电影") # index = D("电影")(index) 先实例化在传入函数 def movie(name): print("Hello {}.".format(name)) if __name__ == '__main__': index('张三') # index('张三') = D()(index)('张三') # D() --> d_obj # d_obj(index) -->1. self.func = args[0] =index; 2. self.mode = "调用" 3.return d_obj #d_obj() --> self.a = None --> print("欢迎来到首页。") --> self.func(*args, **kwargs) = index('张三') --> print("Hello张三.") movie('张三') # movie('张三') = D("电影")(movie)(张三) # movie('张三') = D()(movie)('张三') # D() --> d_obj # d_obj(movie) -->1. self.func = args[0] =movie; 2. self.mode = "调用" 3.return d_obj # d_obj() --> self.a = "电影" --> print("欢迎来到电影页。") --> self.func(*args, **kwargs) = movie('张三') --> print("Hello张三.") #执行结果 """ 欢迎来到首页 hello 张三 欢迎来到电影页 hello 张三 """
2.装饰类:使用装饰器,来批量修改被装饰的类的某些方法
# 定义一个类装饰器 class D(object): def __call__(self, cls): class Inner(cls): # 重写被装饰类的f方法 def f(self): print('Hello 张三.') return Inner @D() # C = D()(C) 先实例化再被调用 C = Inner(C) class C(object): # 被装饰的类 # 有一个实例方法 def f(self): print("Hello world.") if __name__ == '__main__': c = C() #C() = Inner(C)() --> c = inner_obj c.f() # c.f() --> inner_obj.f() --> print('Hello 张三.')