Python装饰器粗解学习
此次学习资料详细来自:http://blog.csdn.net/u013471155
本次是粗学,仍有诸多疑问,暂且记录一二,如有不足和建议,希望可以达者指点。
三个关键点理解:
1、关于函数“变量”(或“变量”函数)的理解
2、关于高阶函数的理解
3、关于嵌套函数的理解
1、装饰器
装饰器实际上就是为了给某程序增添功能,但该程序已经上线或已经被使用,那么就不能大批量的修改源代码,这样是不科学的也是不现实的,因为就产生了装饰器,使得其满足:
1.不能修改被装饰的函数的源代码
2.不能修改被装饰的函数的调用方式
3.满足1、2的情况下给程序增添功能
装饰器的原则组成:
<函数+实参高阶函数+返回值高阶函数+嵌套函数+语法糖 = 装饰器>
2、需求的实现
假设有代码:
import time
def test():
time.sleep(2)
print("test is running")
test()
很显然,这段代码运行的结果一定是:等待约2秒后,输出
test is running
- 那么要求在满足三原则的基础上,给程序添加统计运行时间(2 second)功能
2.1高阶函数
那么对于高阶函数的形式可以有两种:
- 把一个函数名当作实参传给另外一个函数(“实参高阶函数”)
- 返回值中包含函数名(“返回值高阶函数”)
那么这里面所说的函数名,实际上就是函数的地址,也可以认为是函数的一个标签而已,并不是调用,是个名词。如果可以把函数名当做实参,那么也就是说可以把函数传递到另一个函数,然后在另一个函数里面做一些操作,根据这些分析来看,这岂不是满足了装饰器三原则中的第一条,即不修改源代码而增加功能。那我们看来一下具体的做法:
import time def test():
time.sleep(2)
print("test is running") def deco(func):
start = time.time()
func() #
stop = time.time()
print(stop - start) deco(test) #
在#1处,把test当作实参传递给形参func,即func=test。这里传递的是地址,也就是此时func也指向了之前的test所定义的那个函数体,可以说在deco()内部,func就是test。在#2处,把函数后面加上括号,也就是对函数的调用(执行它)。因此,结果如下:
test is running!
the run time is 3.0009405612945557
如果不修改调用方式,就是一定要有test()这条语句,那么就用到了第二种高阶函数,即返回值中包含函数名
如下:
import time def test():
time,sleep(2)
print("test is running!") def deco(func): print(func)
return func t = deco(test) #
#t() #4 test()
我们看这段代码,在#3处,将test传入deco(),在deco()里面操作之后,最后返回了func,并赋值给t。因此这里test => func => t,都是一样的函数体。最后在#4处保留了原来的函数调用方式。
看到这里显然会有些困惑,我们的需求不是要计算函数的运行时间么,怎么改成输出函数地址了。是因为,单独采用第二张高阶函数(返回值中包含函数名)的方式,并且保留原函数调用方式,是无法计时的。如果在deco()里计时,显然会执行一次,而外面已经调用了test(),会重复执行。这里只是为了说明第二种高阶函数的思想,下面才真的进入重头戏。
2.2嵌套函数
嵌套函数指的是在函数内部定义一个函数,而不是调用。另外还有一个题外话,函数只能调用和它同级别以及上级的变量或函数。也就是说:里面的能调用和它缩进一样的和他外部的,而内部的是无法调用的。
统计运行时间,满足三原则如下:
import time def timer(func): # def deco(): start = time.time()
func()
stop = time.time() return deco test = timer(test) # def test():
time.sleep(2)
print("test is running") test() #
首先,在#6处,把test作为参数传递给timer(),此时,在timer()内部,func = test,接下来定义了一个deco()函数,当并未调用,只是在内存中保存了,并且标签为deco。在timer()函数的最后返回deco()的地址deco。
然后再把deco赋值给了test,那么此时test已经不是原来的test了,也就是test原来的那些函数标签掉了,换成deco。那么在#7处调用的实际上是deco()
这段代码在本质上是修改了调用函数,但在表面上并未修改调用方式,而且实现了功能。
那么通俗一点的理解就是:
把函数看成是盒子,test是小盒子,deco是中盒子,timer是大盒子。程序中,把小盒子test传递到大盒子temer中的中盒子deco,然后再把中盒子deco拿出来,打开看看(调用)
这样做的原因是:
我们要保留test(),还要统计时间,而test()只能调用一次(调用两次运行结果会改变,不满足),再根据函数即“变量”,那么就可以通过函数的方式来回闭包。于是乎,就想到了,把test传递到某个函数,而这个函数内恰巧内嵌了一个内函数,再根据内嵌函数的作用域(可以访问同级及以上,内嵌函数可以访问外部参数),把test包在这个内函数当中,一起返回,最后调用这个返回的函数。而test传递进入之后,再被包裹出来,显然test函数没有弄丢(在包裹里),那么外面剩下的这个test标签正好可以替代这个包裹(内含test())。
3、真正的装饰器
根据以上分析,装饰器在装饰时,需要在每个函数前面加上:
test = timer(test)
显然有些麻烦,Python提供了一种语法糖,即:
@timer
这两句是等价的,只要在函数前加上这句,就可以实现装饰作用。
以上为无参形式
4、装饰器有参函数
import time def timer(func): def deco(*args, **kwargs):
start = time.time()
res = func(*args, **kwargs)
stop = time.time()
print(stop - start)
return res
return deco @timer
def test(parameter): # time.sleep(2)
print("test is running!")
return "Returned value" test()
5、带参数的装饰器
又增加了一个需求,一个装饰器,对不同的函数有不同的装饰。那么就需要知道对哪个函数采取哪种装饰。因此,就需要装饰器带一个参数来标记一下。例如:
@decorator(parameter = value)
比如有两个函数:
def task1():
time.sleep(2)
print("in the task1") def task2():
time.sleep(2)
print("in the task2") task1()
task2()
要对这两个函数分别统计运行时间,但是要求统计之后输出:
the task1/task2 run time is : 2.00……
于是就要构造一个装饰器timer,并且需要告诉装饰器哪个是task1,哪个是task2,也就是要这样:
@timer(parameter = 'task1') #
def task1():
time.sleep(2)
print("in the task1") @timer(parameter = 'task2') #
def task2():
time.sleep(2)
print("in the task2") task1()
task2()
那么方法有了,但是我们需要考虑如何把这个parameter参数传递到装饰器中,我们以往的装饰器,都是传递函数名字进去,而这次,多了一个参数,要怎么做呢?
于是,就想到再加一层函数来接受参数,根据嵌套函数的概念,要想执行内函数,就要先执行外函数,才能调用到内函数,那么就有:
def timer(parameter): #
print("in the auth :", parameter) def outer_deco(func):
print("in the outer_wrapper: ", parameter) def deco(*args, **kwargs): return deco return outer_deco
首先timer(parameter),接收参数parameter=’task1/2’,而@timer(parameter)也恰巧带了括号,那么就会执行这个函数, 那么就是相当于:
timer = timer(parameter)
task1 = timer(task1)
后面的运行就和一般的装饰器一样了:
import time def timer(parameter): def outer_wrapper(func): def wrapper(*args, **kwargs): if parameter == 'task1':
start = start.time()
func(*args, **kwargs)
stop = time.time()
print("the task1 run time is :", stop - start) elif parameter == 'task1':
start = start.time()
func(*args, **kwargs)
stop = time.time()
print("the task1 run time is :", stop - start) return wrapper return outer_wrapper @timer(parameter = 'task1')
def task1():
time.sleep(2)
print("in the task1") @timer(parameter = 'task2')
def task2():
time.sleep(2)
print("in the task2") task1()
task2()
Python装饰器粗解学习的更多相关文章
- Python装饰器详解
python中的装饰器是一个用得非常多的东西,我们可以把一些特定的方法.通用的方法写成一个个装饰器,这就为调用这些方法提供一个非常大的便利,如此提高我们代码的可读性以及简洁性,以及可扩展性. 在学习p ...
- Python装饰器的解包装(unwrap)
在Python 3.4 中,新增一个方法unwrap,用于将被装饰的函数,逐层进行解包装. inspect.unwrap(func, *, stop=None) unwrap方法接受两个参数:func ...
- Python—装饰器详解
装饰器:(语法糖) 本质是函数,它是赋予函数新功能,但是不改变函数的源代码及调用方式 原则: 1.不能修改被装饰函数的源代码 2.不能修改被装饰函数的调用方式 3.函数的返回值也不变 这两点简而言 ...
- Python 装饰器学习
Python装饰器学习(九步入门) 这是在Python学习小组上介绍的内容,现学现卖.多练习是好的学习方式. 第一步:最简单的函数,准备附加额外功能 1 2 3 4 5 6 7 8 # -*- c ...
- (转载)Python装饰器学习
转载出处:http://www.cnblogs.com/rhcad/archive/2011/12/21/2295507.html 这是在Python学习小组上介绍的内容,现学现卖.多练习是好的学习方 ...
- Python装饰器学习
Python装饰器学习(九步入门) 这是在Python学习小组上介绍的内容,现学现卖.多练习是好的学习方式. 第一步:最简单的函数,准备附加额外功能 ? 1 2 3 4 5 6 7 8 # -*- ...
- Python装饰器模式学习总结
装饰器模式,重点在于装饰.装饰的核心仍旧是被装饰对象. 类比于Java编程的时候的包装模式,是同样的道理.虽然概念上稍有不同但是原理上还是比较相近的.下面我就来谈一谈我对Python的装饰器的学习的一 ...
- Python 装饰器学习心得
最近打算重新开始记录自己的学习过程,于是就捡起被自己废弃了一年多的博客.这篇学习笔记主要是记录近来看的有关Python装饰器的东西. 0. 什么是装饰器? 本质上来说,装饰器其实就是一个特殊功能的函数 ...
- python设计模式之装饰器详解(三)
python的装饰器使用是python语言一个非常重要的部分,装饰器是程序设计模式中装饰模式的具体化,python提供了特殊的语法糖可以非常方便的实现装饰模式. 系列文章 python设计模式之单例模 ...
随机推荐
- Android Studio无法预览xml布局之解决方法(两种)
学习安卓程序开发,用的Android Studio,发现怎么更改xml代码都没有想要的效果.如图 代码如下: <?xml version="1.0" encoding=&qu ...
- 基于node 搭建http2服务
1.准备工作:安装node2.安装http2: npm install http2 -g安装完成后,在安装目录中appData/Roaming>npm>node_modules>ht ...
- C. Tennis Championship dp递推 || 找规律
http://codeforces.com/contest/735/problem/C C. Tennis Championship time limit per test 2 seconds mem ...
- SAMBA服务初学练习
服务概述 Samba最先在Linux和Windows之间架起了一座桥梁,正是由于Samba的出现,我们可以在Linux和Windows之间实现文件共享的相互通讯,我们可以将其架设成一个功能非常强大的文 ...
- oracle PL、SQL(二)
oracle PL.SQL(基础知识点二) --1,参数 in:表示输入类型,可以省略 :out:输出类型不能省略---------- ----案例1:编写一个过程,可以输入雇员的编号,返回该雇员的姓 ...
- 构建微服务开发环境6————利用npm安装前端框架
[内容指引] 安装JQuery组件: 安装BootStrap前端框架; 安装AngularJs前端框架: 安装angular动画模块: 安装angular的ui-router模块. 一.进入下载的目标 ...
- 《精通css》笔记
第2章 选择器,注释 1.要知道常用选择器(id选择器,类选择器,类型选择器,后代选择器,伪类选择器(文档结构之外)) 通用选择器(*{ }) 高级选择器(子选择器,相邻同胞选择器,属性选 ...
- 字符串逆序-c语言
给定一个含有n个元素的字符串,实现逆序. 这是个很基础的问题,实现方式也是很常见的c语言思路.虽然简单,但是仍然记录下来. [期望] 比如char str[] = "abcdefg" ...
- 自欺欺人的使用 NSTimer 销毁
自欺欺人的使用 NSTimer 销毁 Demo地址 1.NSTimer是要加到runloop中才会起作用. 常见的创建timer方式 // 第一种方式 @property (nonatomic , s ...
- Oracle PL/SQL 编程手册(SQL大全)
一.SQLPLUS 1引言 SQL命令 以下17个是作为语句开头的关键字: alterdroprevoke auditgrantrollback* commit*inse ...