python3-三个demo带你入门装饰器
装饰器入门
在不修改程序源代码和程序调用方式的情况下,扩展程序功能时不得不用到装饰器。
python中的装饰器可谓功能强大,强大到刚接触它就被它弄得措手不及。
但是,静下心来好好研究,那可是回味无穷。于是,整理了下面2个demo,归纳总结。
demo1:简单装饰器
#demo1
def w(func):
def inner():
print("begin...")
func()
print("end...")
return inner
@w #语法糖:@w
def test():
print("我就是那个被装饰的函数")
test()
#output:
begin...
我就是那个被装饰的函数
end...
这就是一个简单的装饰器。所谓装饰指的是在test函数功能的基础上增加一些别的功能,本demo中用两次打印(即:begin...\end...)代替,且调用的方式不变还是原来的 test()。
初看demo1你应该是不明白到底发生了什么,那个@w是什么意思。
其实,@w 放在test函数的定义上面,这种简单的语法是python装饰器的一种简写。这样显的简单大气上档次。
但是,最原始、最本质的其实是高阶函数的使用,涉及到知识点有:
- 函数可以当参数传给另一个参数
- 函数可以当返回值被返回给一个变量,调用该变量就相当于调用被返回的函数
说了这么多,还是不如直接看代码来的快。
#demo1本质
def w(func):
print("我要开始装饰啦...")
def inner():
print("begin...")
func()
print("end...")
return inner
def test():
print("我就是那个被装饰的函数")
test=w(test) #本质所在
test()
你看,test=w(test) 这行语句是多么强大,玩的多么巧妙。
首先,我们应该知道这行语句执行的本质:
- 把test函数名当实参传给w函数,传给w函数中的形参func,
- 另外,w函数中嵌套定义了inner函数,func在inner中被调用,被装饰的test函数在inner中被调用,
- 但是 inner函数在w中没有被调用而是当返回值被返回到w函数外。接收的变量是test,此处的test仅仅是以一个变量名,虽然和需要被装饰的函数test长的一模一样,但本质是不一样的。
- 然后,程序的最后调用 test(),本质不是调用需要被装饰的那个test函数,而是调用了w函数的返回的函数 inner。
- 于是,直到现在被装饰的函数test,才在inner调用被执行中间调用。
这就是装饰的全部过程。总结:使用test=w(test)之后,调用test(),就相当于调用了inner()。
所以现在明白了demo1的output输出结果的顺序了吗?
因为首先调用的是w函数,然后遇到w函数返回inner函数,再开始调用inner函数。
现在回头看,@w放在test函数的定义前,其实很简单很方便地隐藏了私底下那些“见不得人”的操作。
不过这样的好处是显而易见的,官方取名为 语法糖,简单形象。
demo2:demo1补充
为了加深装饰器的本质执行流程,还有必要再唠叨两句,直接看代码。
#demo2:
def w(func):
print("我要开始装饰啦....") #w中第一条语句
def inner(): #w中第二条语句
print("begin...")
func()
print("end...")
print("好了装饰完毕了吗?") #w中第三条语句
return inner
@w #语法糖:@w
def test():
print("我就是那个被装饰的函数")
test()
你猜,输出结果会是神马样子,是你想的那个样子吗?
我还是直接说吧,其实我在代码中加了注释的。
我们知道,装饰器的本质执行语句是:test=w(test)
- 首先,是执行w函数,那就从上往下,分别还行语句1、语句2、语句3。。。
- 所以,首先执行的是语句1,打印 :我要开始装饰啦....
- 然后,语句2(其实这样说不准确),但是语句2是函数的定义,在w中并未被调用,所以此时并不会有输出,
- 再其次是语句3,打印:好了装饰完毕了吗?
- 于是w函数执行完毕了,遇到了return返回,返回的是inner函数名给test这个变量名
- 最后的最后,调用test加(),即执行inner函数,于是打印:begin... 等
所以打印结果如下:
#output:
我要开始装饰啦....
好了装饰完毕了吗?
begin...
我就是那个被装饰的函数
end...
demo3:两个装饰器
一个函数被两个装饰器装饰,是检验你是否理解装饰器的好东西。
#demo3:
def w1(func):
print("我是w1")
def inner():
print("w1,begin...")
func()
print("w1,end...")
return inner
def w2(func):
print("我是w2")
def inner():
print("w2,begin...")
func()
print("w2,end...")
return inner
@w2
@w1
def test():
print("我是那个被装饰的函数")
test()
像上面的思路一样,我们来捋一遍。
demo3中两个语法糖叠加的效果等同于:test=w2(w1(test))
因为两个装饰器w1、w2里面都有参数func和inner函数,可能容易混乱。
所以,下面叙述中提到的func1指的是w1函数的func,inner2指的是w2的inner,其他类似。
#1 按照从左到右的执行顺序,test=w2(w1(test)),首先是把需要被装饰的test函数当实参传给w1函数中的形参func1;
#2 w1函数就会执行,执行相关语句知道遇到return语句返回inner1,此时外面还有一个装饰器,inner1就不是直接返回给一个变量名,而是返回给w2当参数,即func2接收实参inner1;
#3 w2函数就会执行,执行相关语句知道遇到return语句返回inner2,给变量test;
#4 调用加括号的test,就相当于调用inner2(),此时inner2中的func2是inner1,因为#2中func2接收的实参是inner1呀;
#5 当func2加括号调用时,就是调用inner1,此时inner1中的func1是需要被装饰的test,因为#1中func1接收的实参是test呀。
总结:
调用test()执行步骤如下:
step1:执行w1,func1形参接收实参test,开始执行语句,先输出“我是w1”,再返回inner1给w2
step2:执行w2,func2形参接收实参inner1,开始执行语句,先输出“我是w2”,再返回inner2给test变量
step2:执行test(),就是执行inner2(),于是输出“w2,begin”,调用func2(),此即调用inner1,
step3:输出“w1.begin”,再调用func1,即调用需要被装饰的test函数,输出“我是那个被装饰的函数”,
step4:然后依次输出,“w1,end”
step5:此时inner2()中的func2执行完毕,再执行print("w2,end..."),输出“w2,end...”。
#output:
我是w1
我是w2
w2,begin...
w1,begin...
我是那个被装饰的函数
w1,end...
w2,end...
总结:
#test(w2(w1(test)))
test()
- 多个装饰器装饰,靠近被装饰函数的先执行
- w1装饰器中:func是被装饰的函数test,
- w2装饰器中:func是w1的返回值inner,
- 最后,调用 变量test(),即执行w2的inner()
python3-三个demo带你入门装饰器的更多相关文章
- 08 . Python3高阶函数之迭代器、装饰器
Python3高阶函数之迭代器.装饰器 列表生成式 推导式就是构建比较有规律的列表,生成器. 孩子,我现在有个需求,看列表[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],我要求你把列表里 ...
- Python带参数的装饰器
在装饰器函数里传入参数 # -*- coding: utf-8 -*- # 2017/12/2 21:38 # 这不是什么黑魔法,你只需要让包装器传递参数: def a_decorator_passi ...
- 学以致用三十六-----弄懂python装饰器
看了海峰老师讲解的装饰器视频,讲解的非常棒.根据视频,记录笔记如下: 装饰器: 1.本质是函数,用def来定义.功能就是用来(装饰)其他函数,为其他函数添加附加功能 现有两个函数如下, def tes ...
- Python 带参数的装饰器 [2] 函数参数类型检查
在Python中,不知道函数参数类型是一个很正常的事情,特别是在一个大项目里.我见过有些项目里,每一个函数体的前十几行都在检查参数类型,这实在是太麻烦了.而且一旦参数有改动,这部分也需要改动.下面我们 ...
- python 全栈开发,Day12(函数的有用信息,带参数的装饰器,多个装饰器装饰一个函数)
函数的执行时,*打散.函数的定义时,*聚合. from functools import wraps def wrapper(f): # f = func1 @wraps(f) def inner(* ...
- Python之函数的进阶(带参数的装饰器)
函数篇--装饰器二 带参数的装饰器 def outer(flag): def timer(func): def inner(*args,**kwargs): if flag: print('''执行函 ...
- Python_函数的有用信息、带参数的装饰器、多个装饰器装饰一个函数
函数的有用信息 代码1: def login(username, password): """ 此函数需要用户名,密码两个参数,完成的是登录的功能. :return: T ...
- python:带参数的装饰器,函数的有用信息
一.带参数的装饰器,函数的有用信息 def func1(): '''此函数的功能是完成的登陆的功能 return: 返回值是登陆成功与否(true,false) ''' print(333) func ...
- python全栈开发day12-函数的有用信息、带参数的装饰器、多个装饰器装饰一个函数、global和nonlocal的进一步解析和总结
1.上周回顾 1).函数名的应用 直接打印函数名,是函数的地址 变量 函数的参数 函数的返回值 可以当容器类数据类型的元素 2).闭包 内层函数对外层函数的非全局变量的引用,就是闭包. 并返回内部函数 ...
随机推荐
- 1-for循环套生成器的面试题
参考自: https://www.cnblogs.com/shuimohei/p/9686578.html https://segmentfault.com/a/1190000016577353 题目 ...
- [Interview] Bubble sort using singly-linked list
Question : Bubble sort using singly-linked list 群暉面試題 Idea : 在linked list 交換node與node時, 我們會想用換*next ...
- Linux之systemd服务配置及自动重启
layout: post title: Linux之systemd服务配置及自动重启 date: 2019-09-09 tags: linux --- Linux之systemd服务配置及自动重启 0 ...
- 20180209-json&pickle&shelve模块
什么是序列化? 序列化就是把内存里的数据类型转成字符串,以使其能够存储到硬盘中或在网络中传输到远程,因为硬盘和网络传输时只接收bytes 用于序列化的两个模块 1. json,用于字符串和python ...
- Django学习笔记--数据库中的单表操作----增删改查
1.Django数据库中的增删改查 1.添加表和字段 # 创建的表的名字为app的名称拼接类名 class User(models.Model): # id字段 自增 是主键 id = models. ...
- SERVLET API中转发与重定向的区别?
SERVLET API中转发与重定向的区别? 1.转发(forward方法) 转发仅是容器中控制权的转向,在客户端浏览器地址栏中不会显示出转向后的地址. 转发是服务器请求资源,服务器直接访问目标地址的 ...
- CONNECT_BY_ROOT
1.select * from EMP t where t.deptno = 10; EMPNO ENAME JOB MGR HIREDATE SAL ...
- MyEclipse使用过程中的问题及对应设置的总结
1.关闭 Javaweb项目中的updating index : Window => Preferences => Myeclipse Enterprise Workbench => ...
- 趣头条基于 Flink 的实时平台建设实践
本文由趣头条实时平台负责人席建刚分享趣头条实时平台的建设,整理者叶里君.文章将从平台的架构.Flink 现状,Flink 应用以及未来计划四部分分享. 一.平台架构 1.Flink 应用时间线 首先是 ...
- webpack使用问题记录
1.版本问题 webpack的安装并不是越新越好.当使用最新版本时不使用配置文件,直接编译不生成文件甚至报错:使用3.12版本时编译文件可以成功但是不生成新文件:使用 3.5.3 版本时成功且生成新文 ...