首先装饰器实现的条件:

高阶函数+嵌套函数 =》装饰器

1.首先,我们先定义一个高级函数,去装饰test1函数,得不到我们想要的操作方式

  1. import time
  2.  
  3. #定义高阶函数
  4. def deco(func):
  5. start_time = time.time()
  6. func()
  7. stop_time = time.time()
  8. print("the func run time is %s"%(stop_time-start_time))
  9.  
  10. #装饰test1函数
  11.  
  12. def test1():
  13. time.sleep(3)
  14. print("in the test1")
  15. #deco(test1) #这样操作就改变函数的调用方式 , 怎样操作不改变调用方式
  16. test1 = deco(test1) #想想前提是用return 返回值
  17.  
  18. #输出
  19. in the test1
  20. the func run time is 3.0002999305725098

2 .用高阶函数的返回值方法,得到了想要的调用方式,但是结果不是想要的,一直到现在我们一直在用高阶函数

  1. import time
  2.  
  3. #定义高阶函数
  4. def deco(func):
  5. start_time = time.time()
  6. return func()
  7. stop_time = time.time()
  8. print("the func run time is %s"%(stop_time-start_time))
  9.  
  10. #装饰test1函数
  11.  
  12. def test1():
  13. time.sleep(3)
  14. print("in the test1")
  15.  
  16. test1() #调用方式问题解决了,但是结果不是想要的结果
  17.  
  18. 结果
  19. in the test1

3 . 再用嵌套函数得到了我们想要的结果和不变的调用方式(哇喔,是不是好牛X)

  1. import time
  2.  
  3. #定义内置函数
  4. def timmer(func): #timmer(test1) func=test1
  5. def deco():
  6. start_time = time.time()
  7. func() #run test1()
  8. stop_time = time.time()
  9. print("the func run time is %s"%(stop_time-start_time))
  10.  
  11. return deco #重点的重点
  12.  
  13. #装饰test1函数
  14. @timmer # 相当于test1 = timmer(test1) 牛X的方法
  15. def test1():
  16. time.sleep(3)
  17. print("in the test1")
  18.  
  19. #直接执行test1函数
  20. test1()
  21.  
  22. #输出
  23. in the test1
  24. the func run time is 3.0002999305725098

4.  我们用装饰器去装饰一个带参数的函数会怎么样?

  1. import time
  2.  
  3. def timmer(func):
  4. def deco():
  5. start_time = time.time()
  6. func() #run test2() 这里没有参数,报错
  7. stop_time = time.time()
  8. print("the func run time is %s"%(stop_time-start_time))
  9.  
  10. return deco
  11.  
  12. @timmer
  13. def test2(name,age):
  14. print("name:%s,age:%s"%(name,age))
  15.  
  16. test2()
  17.  
  18. #输出
  19. Traceback (most recent call last):
  20. File "D:/PycharmProjects/pyhomework/day4/装饰器/装饰器高潮.py", line 16, in <module>
  21. test2()
  22. File "D:/PycharmProjects/pyhomework/day4/装饰器/装饰器高潮.py", line 6, in deco
  23. func() #run test2()
  24. TypeError: test2() missing 2 required positional arguments: 'name' and 'age' #缺少传入name和age参数

结果:很显然是错误的。因为这边执行的test2函数其实就是执行的deco函数,deco函数体内的func()其实就是执行test2函数,但是,test2需要传入name和age两个参数,所以报错。那怎么解决呢?

5. 我们只能传入参数了,但是你又不能确定传入几个参数,所以我们只能用非固定参数传参。代码如下:(哇喔,这个更牛X)

  1. import time
  2.  
  3. def timmer(func): #timmer(test1) func=test1
  4. def deco(*args,**kwargs): #传入非固定参数 可以面对各种参数
  5. start_time = time.time()
  6. func(*args,**kwargs) #传入非固定参数
  7. stop_time = time.time()
  8. print("the func run time is %s"%(stop_time-start_time))
  9.  
  10. return deco
  11.  
  12. #不带参数
  13. @timmer # 相当于test1 = timmer(test1)
  14. def test1():
  15. time.sleep(3)
  16. print("in the test1")
  17.  
  18. #带参数
  19. @timmer
  20. def test2(name,age):
  21. print("name:%s,age:%s"%(name,age))
  22. #调用
  23. test1()
  24. test2("qianduoduo",22)
  25.  
  26. #输出
  27. #test1
  28. in the test1
  29. the func run time is 3.0010883808135986
  30. #test2
  31. name:qianduoduo,age:22
  32. the func run time is 0.0 #test2

6.我就问还有没有终极版的,答案是:有

前面的都是小高潮(基本上也能满足百分之九十的需求),现在正式介绍终极的高潮版的 语法糖(我也不知道为什么取这个名字)也叫装饰器

  1. import time
  2. user,passwd = 'qian','zxc123'
  3. def auth(func):
  4. def wrapper(*args,**kwargs): #包装
  5. username = input("Username:").strip()
  6. password = input("Password:").strip()
  7.  
  8. if user == username and passwd == password:
  9. print("\033[32;1mUser has passed authentication\033[0m")
  10. func(*args,**kwargs)
  11. else:
  12. exit("\033[31;1mInvalid username or password\033[0m")
  13. return wrapper
  14.  
  15. def index(): #建立网站的首页 首页不需要登入
  16. print("welcome to index page")
  17. @auth #加上装饰器
  18. def home(): #自己的页面,需要登入
  19. print("welcome to home page")
  20. @auth
  21. def bbs(): #论坛区的页面,需要登入
  22. print("welcome to bbs page")
  23.  
  24. index()
  25.  
  26. home()
  27.  
  28. bbs()
  29. 运行结果: #运行很完美,堪称经典之作
  30. welcome to index page
  31. Username:qian
  32. Password:zxc123
  33. User has passed authentication
  34. welcome to home page
  35. Username:qian
  36. Password:zxc123
  37. User has passed authentication
  38. welcome to bbs page

7.走到这地方,我的问题来了,我的home执行完没有任何数据,我现在在home中return一个数据,看代码:

  1. import time
  2. user,passwd = 'qian','zxc123'
  3. def auth(func):
  4. def wrapper(*args,**kwargs): #包装
  5. username = input("Username:").strip()
  6. password = input("Password:").strip()
  7.  
  8. if user == username and passwd == password:
  9. print("\033[32;1mUser has passed authentication\033[0m")
  10. func(*args,**kwargs)
  11. else:
  12. exit("\033[31;1mInvalid username or password\033[0m")
  13. return wrapper
  14.  
  15. def index(): #建立网站的首页 首页不需要登入
  16. print("welcome to index page")
  17. @auth #加上装饰器
  18. def home(): #自己的页面,需要登入
  19. print("welcome to home page")
  20. return "from home" #假设这就是我返回的数据
  21. @auth
  22. def bbs(): #论坛区的页面,需要登入
  23. print("welcome to bbs page")
  24.  
  25. index()
  26.  
  27. print(home()) #home的执行结果
  28.  
  29. bbs()
  30.  
  31. 结果:
  32. welcome to index page
  33. Username:qian
  34. Password:zxc123
  35. User has passed authentication
  36. welcome to home page
  37. None # home的执行结果,没有执行结果
  38. #(虽然装饰器没改变源代码,没改变调用方式,但是改变了执行结果)
  39. # 这时候我们怎么办,怎么获得home的返回结果?
  40. Username:

8 我们来分析一下,按照顺序①②③

  1. import time
  2. user,passwd = 'qian','zxc123'
  3. def auth(func):
  4. def wrapper(*args,**kwargs): #包装
  5. username = input("Username:").strip()
  6. password = input("Password:").strip()
  7.  
  8. if user == username and passwd == password:
  9. print("\033[32;1mUser has passed authentication\033[0m")
  10. func(*args,**kwargs) #from home
  11. # ① func的执行结果没有传给谁,那就没了,就是None
  12. else:
  13. exit("\033[31;1mInvalid username or password\033[0m")
  14. return wrapper #③wrapper执行了,没有返回值
  15.  
  16. def index(): #建立网站的首页 首页不需要登入
  17. print("welcome to index page")
  18. @auth #加上装饰器
  19. def home(): #自己的页面,需要登入
  20. print("welcome to home page")
  21. return "from home"
  22. @auth
  23. def bbs(): #论坛区的页面,需要登入
  24. print("welcome to bbs page")
  25.  
  26. index()
  27.  
  28. print(home()) #②调用home 时候,就是调用wrapper()
  29. bbs()

9 分析完得出以下代码:

  1. import time
  2. user,passwd = 'qian','zxc123'
  3. def auth(func):
  4. def wrapper(*args,**kwargs): #包装
  5. username = input("Username:").strip()
  6. password = input("Password:").strip()
  7.  
  8. if user == username and passwd == password:
  9. print("\033[32;1mUser has passed authentication\033[0m")
  10. # return func(*args,**kwargs) 函数到这里就结束了
  11. res = func(*args,**kwargs) #增加变量
  12. print("---after authenticaion") #再装饰点其他东西
  13. return res
  14. else:
  15. exit("\033[31;1mInvalid username or password\033[0m")
  16. return wrapper
  17.  
  18. def index(): #建立网站的首页 首页不需要登入
  19. print("welcome to index page")
  20. @auth #加上装饰器
  21. def home(): #自己的页面,需要登入
  22. print("welcome to home page")
  23. return "from home"
  24. @auth
  25. def bbs(): #论坛区的页面,需要登入
  26. print("welcome to bbs page")
  27.  
  28. index()
  29.  
  30. print(home())
  31.  
  32. bbs()
  33.  
  34. 结果
  35. welcome to index page
  36. Username:qian
  37. Password:zxc123
  38. User has passed authentication
  39. welcome to home page
  40. ---after authenticaion
  41. from home
  42. Username:qian
  43. Password:zxc123
  44. User has passed authentication
  45. welcome to bbs page
  46. ---after authenticaion

10不过这不是我要讲的高潮部分,而是补充前面的前奏

同志们在真正的现实生活中,账号认证方式有多种,现在的认证方式是本地认证,下面才是真正的终极

  1. import time
  2. user,passwd = 'qian','zxc123'
  3. def auth(auth_type):
  4. print("auth func:",auth_type)
  5. def outer_wrapper(func):
  6. def wrapper(*args,**kwargs): #包装
  7. if auth_type == "local":
  8. username = input("Username:").strip()
  9. password = input("Password:").strip()
  10. if user == username and passwd == password:
  11. print("\033[32;1mUser has passed authentication\033[0m")
  12. res = func(*args,**kwargs)
  13. print("---after authenticaion")
  14. return res
  15. else:
  16. exit("\033[31;1mInvalid username or password\033[0m")
  17. elif auth_type == "ldap":
  18. print("搞毛线ldap,不会")
  19. return wrapper
  20. return outer_wrapper
  21.  
  22. def index(): #
  23. print("welcome to index page")
  24. @auth(auth_type = "local") #本地的文件认证 home = wrapper
  25. def home():
  26. print("welcome to home page")
  27. return "from home"
  28. @auth(auth_type ="ldap") #ldap认证
  29. def bbs():
  30. print("welcome to bbs page")
  31.  
  32. index()
  33.  
  34. print(home()) #wrapper()
  35.  
  36. bbs()
  37.  
  38. 结果
  39. auth func: local
  40. auth func: ldap
  41. welcome to index page
  42. Username:qian
  43. Password:zxc123
  44. User has passed authentication
  45. welcome to home page
  46. ---after authenticaion
  47. from home #调用方式 home() 就没这个返回值了
  48. 搞毛线ldap,不会

这就是我们的终极版本, 运行的具体顺序可以自己调试调试

这个函数的作用分别是:

  1.auth(auth_type) 传递装饰器的参数

  2.outer_wrapper(func) 把函数当做实参传递进来

  3.wrapper(*args,**kwargs) 真正执行装饰的函数

  1. ##home()

小白的Python之路 day4 装饰器高潮的更多相关文章

  1. 小白的Python之路 day4 装饰器前奏

    装饰器前奏: 一.定义: 1.装饰器本质是函数,语法都是用def去定义的 (函数的目的:他需要完成特定的功能) 2.装饰器的功能:就是装饰其他函数(就是为其他函数添加附加功能) 二.原则: 1. 不能 ...

  2. python之路之装饰器

    一 装饰器进化之路1) import time def index(): start_time=time.time() time.sleep() print('welcome to index wor ...

  3. [Python之路] 使用装饰器给Web框架添加路由功能(静态、动态、伪静态URL)

    一.观察以下代码 以下来自 Python实现简易HTTP服务器与MINI WEB框架(利用WSGI实现服务器与框架解耦) 中的mini_frame最后版本的代码: import time def in ...

  4. 小白的Python之路 day4 生成器

    一.列表生成式  看下面例子: 列表生成式的作用:主要是让代码更简洁(还有装X的效果) 二.生成器 通过列表生成式,我们可以直接创建一个列表.但是,受到内存限制,列表容量肯定是有限的.而且,创建一个包 ...

  5. 小白的Python之路 day4 迭代器

    迭代器 学习前,我们回想一下可以直接作用于for循环的数据类型有以下几种: 1.集合数据类型,如list.tuple.dict.set.str等: 2.是generator,包括生成器和带yield的 ...

  6. 小白的Python之路 day4 json and pickle数据标准序列化

    一.简述 我们在写入文件中的数据,只能是字符串或者二进制,但是要传入文件的数据不一定全是字符串或者二进制,那还要进行繁琐的转换,然后再读取的时候,还要再转回去,显得很麻烦,今天就来学习标准的序列化:j ...

  7. 小白的Python之路 day4 软件目录结构规范

    软件目录结构规范 为什么要设计好目录结构? "设计项目目录结构",就和"代码编码风格"一样,属于个人风格问题.对于这种风格上的规范,一直都存在两种态度: 一类同 ...

  8. 小白的Python之路 day4 不同目录间进行模块调用(绝对路径和相对路径)

    一.常用模块调用函数功能解释 1.__file__ 功能:返回自身文件的相对路径 你从pycharm的执行结果可以看出,在pycharm执行atm.py文件时,是从绝对路径下去执行的,而你从cmd下去 ...

  9. 小白的Python之路 day4 生成器并行运算

    一.概述 我们已经明白生成器内部的结构,其实就是通过像函数这样的东西实现的! 多线程和单线程:简单来说多线程就是并行运算,单线程就是串行运算 二.生成器执行原理 第一步:生成一个生成器  第二步:执行 ...

随机推荐

  1. PHP时间戳和日期互转换

    在php中我们要把时间戳转换日期可以直接使用date函数来实现,如果要把日期转换成时间戳可以使用strtotime()函数实现,下面我来给大家举例说明. 1.php中时间转换函数 strtotime ...

  2. JavaScript学习笔记(十五)——对象之Date,RegExp

    在学习廖雪峰前辈的JavaScript教程中,遇到了一些需要注意的点,因此作为学习笔记列出来,提醒自己注意! 如果大家有需要,欢迎访问前辈的博客https://www.liaoxuefeng.com/ ...

  3. js学习笔记(延时器)

    //setTimeout()   //功能:设置一个延时器   //语法:var timer = window.setTimeout(code,millisec);   //参数: code:是任何合 ...

  4. PCA, SVD以及代码示例

    本文是对PCA和SVD学习的整理笔记,为了避免很多重复内容的工作,我会在介绍概念的时候引用其他童鞋的工作和内容,具体来源我会标记在参考资料中. 一.PCA (Principle component a ...

  5. kubernetes nginx ingress 使用记录

    前言 ingress是一种可以暴露k8s集群内部service的方式,用户编辑配置文件定义一个ingress资源即可实现外部网络访问内网service. ingress controller是来管理所 ...

  6. c++用类写栈和队列的简单基本操作(实验)

    继续更文.这次用类来写栈和队列,都是用数组模拟的: 以下是栈和队列的定义: 然后分别是栈和队列的类: 完整代码贴上: 栈 //使用类来定义栈 class ZHAN { public: STACK s; ...

  7. setTimeout和setInterval和单线程

    我们知道,js是单线程执行的(单线程j就是说在程序执行时,所走的程序路径按照连续顺序排下来,前面的必须处理好,后面的才会执行).所以其实setTimeout和setInterval所谓的"异 ...

  8. JS对象属性命名规则

    JS标识符的命名规则,即变量的命名规则: 标识符只能由字母.数字.下划线和'$'组成 数字不可以作为标识符的首字符 对象属性的命名规则 通过[]操作符为对象添加属性时,属性名称可以是任何字符串(包括只 ...

  9. Java爬虫——B站弹幕爬取

    如何通过B站视频AV号找到弹幕对应的xml文件号 首先爬取视频网页,将对应视频网页源码获得 就可以找到该视频的av号aid=8678034 还有弹幕序号,cid=14295428 弹幕存放位置为  h ...

  10. 》》stroll--各种特效下拉菜单

    <!doctype html> <html lang="en"> <head> <meta charset="utf-8&quo ...