博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
「Python」一文读懂装饰器
阅读量:7115 次
发布时间:2019-06-28

本文共 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 的执行调用过程

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

转载地址:http://iywel.baihongyu.com/

你可能感兴趣的文章
Android应用程序启动过程源代码分析(5)
查看>>
查询整个数据库中某个特定值所在的表和字段的方法
查看>>
在存储过程中编写正确的事务处理代码(SQL Server 2000 & 2005)
查看>>
数据库邮件
查看>>
adstrtal.sh报超时错误 ERROR : Timed out( 100000 ): Interrupted Exception
查看>>
一个前端工程师的基本修养
查看>>
一个android版本的rss阅读器--明天补充实现过程,先上图
查看>>
JavaScript:文本域事件处理
查看>>
一步一步教你使用AgileEAS.NET基础类库进行应用开发-基础篇-演示ORM中的查询
查看>>
《C#高级编程》笔记系列--点滴记录(持续更新中……)
查看>>
采用泳道图工具跟踪项目进度或者问题解决进度
查看>>
sql server 2008学习1–系统数据库
查看>>
从微软的DBML文件中我们能学到什么(它告诉了我们什么是微软的重中之重)~三 分部类是否破坏了单一职责...
查看>>
HDU1004 Let the Balloon Rise
查看>>
value toDF is not a member of org.apache.spark.rdd.RDD
查看>>
jRating评分插件使用
查看>>
高性能MySQL——Count(1) OR Count(*)?
查看>>
Spark源码分析之Worker
查看>>
JVM系列三:JVM参数设置、分析
查看>>
CAS与spring3集成
查看>>