本文共 4041 字,大约阅读时间需要 13 分钟。
在学习装饰器之前我们先了解下什么是闭包。
闭包
def test1(): print("--- in test1 func----") # 调用函数 test1() # 引用函数 ret = test1 print(id(ret)) print(id(test1)) #通过引用调用函数 ret()
运行结果:
--- in test1 func---- 140212571149040 140212571149040 --- in test1 func----
我们发现 test1() 和 test1 是有区别的,前者是直接调用函数,后者只是函数的引用.
# 定义一个函数 def test(number): # 在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包 def test_in(number_in): print("in test_in 函数, number_in is %d" % number_in) return number+number_in # 其实这里返回的就是闭包的结果 return test_in # 给test函数赋值,这个20就是给参数number,此时ret是 test_in 的引用 ret = test(20) # 注意这里的100其实给参数number_in print(ret(100)) #注 意这里的200其实给参数number_in print(ret(200))
运行结果:
in test_in 函数, number_in is 100 120 in test_in 函数, number_in is 200 220
闭包实例
假如我们要求直线 y=ax+b 的长度.
# 第1种 k = 1 b = 2 y = k*x+b # 缺点:如果需要多次计算,那么就的写多次y = k*x+b这样的式子 y = k * 1 + b y = k * 2 + b # 第2种 def line_2(k, b, x): print(k*x+b) line_2(1, 2, 0) line_2(1, 2, 1) line_2(1, 2, 2) line_2(3,4,0) line_2(3,4,1) line_2(3,4,2) # 缺点:如果想要计算多次这条线上的y值,那么每次都需要传递k,b的值,麻烦 print("-"*50) # 第3种: 全局变量 k = 1 b = 2 def line_3(x): print(k*x+b) line_3(0) line_3(1) line_3(2) k = 11 b = 22 line_3(0) line_3(1) line_3(2) # 缺点:如果要计算多条线上的y值,那么需要每次对全局变量进行修改,代码会增多,麻烦 print("-"*50) # 第4种:缺省参数 def line_4(x, k=1, b=2): print(k*x+b) line_4(0) line_4(1) line_4(2) line_4(0, k=11, b=22) line_4(1, k=11, b=22) line_4(2, k=11, b=22) # 优点:比全局变量的方式好在:k, b是函数line_4的一部分 而不是全局变量,因为全局变量可以任意的被其他函数所修改 # 缺点:如果要计算多条线上的y值,那么需要在调用的时候进行传递参数,麻烦 print("-"*50) # 第5种:实例对象 class Line5(object): def __init__(self, k, b): self.k = k self.b = b def __call__(self, x): print(self.k * x + self.b) line5_1_2 = Line5(1, 2) # 对象.方法() # 对象() line5_1_2(0) line5_1_2(1) line5_1_2(2) line5_11_22 = Line5(11, 22) line5_11_22(0) line5_11_22(1) line5_11_22(2) line5_1_2(3) # 缺点:为了计算多条线上的y值,所以需要保存多个k, b的值,因此用了很多个实例对象, 浪费资源 print("-"*50) # 第6种:闭包 def line_6(k, b): def create_y(x): print(k*x+b) return create_y line_6_1 = line_6(1, 2) line_6_1(0) line_6_1(1) line_6_1(2) line_6_2 = line_6(11, 22) line_6_2(0) line_6_2(1) line_6_2(2)
可以发现:
修改外部函数中的变量
# 使用关键字 nonlocal 即可修改外部函数的变量 def func1(a): def func2(): nonlocal a a += 1 return a return func2
装饰器
装饰器本质上就是闭包,装饰器在不改变原函数的定义的条件下,给原函数扩展功能.
下面我们来分析这段代码:
1 def out(func): 2 def inner(): 3 print('正在装饰') 4 func() 5 return inner 6 @out 7 def test(): 8 print('我被装饰啦!') 9 test()
func 的执行调用过程
多个装饰器装饰一个函数
def message(func): print("1. 正在添加短信的装饰功能") def message_pay(): print("----正在做短信安全验证-----") func() return message_pay def gesture(func): print("2. 正在添加手势的装饰功能") def gesture_pay(): print("----正在做手势密码安全验证---") func() return gesture_pay # 短信安全验证 和 手势密码安全验证 @gesture # pay = gesture(pay) @message # pay = message(pay) def pay(): print("---正在支付中---") # 装饰器装饰函数是自动执行 pay()
打印结果:
1. 正在添加短信的装饰功能 2. 正在添加手势的装饰功能 ----正在做手势密码安全验证--- ----正在做短信安全验证----- ---正在支付中---
类装饰器
# 类对象可以的当做 装饰器函数 class Person(object): def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): # print("我被调用啦哟") self.func() # 类装饰器 @Person # t = Person(t) 将函数当做参数传递 __init__() takes 1 positional argument but 2 were given def t(): print("我是被装饰函数....") # 'Person' object is not callable # 调用对象 需要实现 __call__方法 t()
类装饰器平时用的较少,了解即可
带参数的装饰器
def set_level(level): def set_fun(func): def call_func(): if level == 1: print("正在做手势安全验证.....") elif level == 2: print("正在做短信验证码验证.....") func() return call_func return set_fun @set_level(1) # 1. 调用函数 set_level(1), 返回set_fun, 2. 让set_fun作为装饰器函数 login = set_fun(login) def login(): print("-----正在登陆中-------") @set_level(2) def pay(): print("-----正在支付中-------") login() pay()
带参数的装饰器可以这样理解:,最外层将相当于一般的函数,只是将参数给里面的装饰器,实际上里面的闭包才是装饰器.
私信我获取 python 全套资料
转载地址:http://iywel.baihongyu.com/