「Python」一文读懂装饰器

在学习装饰器之前我们先了解下什么是闭包。

闭包

  • 函数引用
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 的执行调用过程

  1. 第1行:out 装饰器函数
  2. 第6行:底层实现 test = out(test) ,不会执行,需要等待
  3. 第7行:定义 func 函数对象
  4. 第6行:先执行右边,在执行左边.func-->test,test-->inner
  5. 第9行:test(),本质上调用 inner()
  6. 第3行
  7. 第4行
  8. 第8行

多个装饰器装饰一个函数

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 全套资料

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值