Python 装饰器的形成过程
装饰器
定义:本质是函数,(装饰其他函数),即为其他函数添加附加功能。
原则: 1、不能修改被装饰的函数的源代码;
2、不能修改被装饰的函数的调用方式。
实现装饰器知识储备:
1. 函数即'变量'
2. 高阶函数
a. 把一个函数名当作实参传递给另一个函数(在不修改被装饰函数源代码的前提下为其添加新功能)
b. 返回值中包含函数名(不修改函数的调用方式)
3. 嵌套函数
高阶函数 + 嵌套函数 (组成)--> 装饰器
1、必备
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: antcolonies '''第一波''' def foo():
print('in the foo') foo # 函数名,相当于变量
print(foo) # <function foo at 0x0000000001CFCBF8> 变量的索引地址
foo() # in the foo 执行/调用函数foo '''第二波''' def foo(x):
print('in the foo_%d' %x) foo = lambda x:x+1 print(foo) print(foo(1)) # 执行下面的lambda表达式,而不再是原来的foo函数,因为函数 foo 被重新定义了 '''
<function foo at 0x0000000001CFCBF8>
in the foo
<function <lambda> at 0x0000000001CFCBF8>
2
'''
2、需求来了
初创公司有N个业务部门,1个基础平台部门,基础平台负责提供底层的功能,如:数据库操作、redis调用、监控API等功能。业务部门使用基础功能时,只需调用基础平台提供的功能即可。如下:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: antcolonies ############### 基础平台提供的功能如下 ############### def f1():
print('in the f1') def f2():
print('in the f2') def f3():
print('in the f3') def f4():
print('in the f4') ############### 业务部门A 调用基础平台提供的功能 ############### f1()
f2()
f3()
f4() ############### 业务部门B 调用基础平台提供的功能 ############### f1()
f2()
f3()
f4()
function call
目前公司有条不紊的进行着,但是,以前基础平台的开发人员在写代码时候没有关注验证相关的问题,即:基础平台的提供的功能可以被任何人使用。现在需要对基础平台的所有功能进行重构,为平台提供的所有功能添加验证机制,即:执行功能前,先进行验证。
老大把工作交给 Low B,他是这么做的:
跟每个业务部门交涉,每个业务部门自己写代码,调用基础平台的功能之前先验证。诶,这样一来基础平台就不需要做任何修改了。
当天Low B 被开除了...
老大把工作交给 Low BB,他是这么做的:
只对基础平台的代码进行重构,让N业务部门无需做任何修改。
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: antcolonies ############### 基础平台提供的功能如下 ############### def f1():
# 验证1
# 验证2
# 验证3
print('in the f1') def f2():
# 验证1
# 验证2
# 验证3
print('in the f2') def f3():
# 验证1
# 验证2
# 验证3
print('in the f3') def f4():
# 验证1
# 验证2
# 验证3
print('in the f4') ############### 业务部门不变 ###############
### 业务部门A 调用基础平台提供的功能### f1()
f2()
f3()
f4() ### 业务部门B 调用基础平台提供的功能 ### f1()
f2()
f3()
f4()
authoration
过了一周 Low BB 被开除了...
老大把工作交给 Low BBB,他是这么做的:
只对基础平台的代码进行重构,其他业务部门无需做任何修改
############### 基础平台提供的功能如下 ############### def check_login():
# 验证1
# 验证2
# 验证3
pass def f1():
check_login()
print 'f1' def f2():
check_login()
print 'f2' def f3():
check_login()
print 'f3' def f4():
check_login()
print 'f4'
authoration_1
老大看了下Low BBB 的实现,嘴角漏出了一丝的欣慰的笑,语重心长的跟Low BBB聊了个天:
老大说:
写代码要遵循开发封闭原则,虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即:
- 封闭:已实现的功能代码块
- 开放:对扩展开发
如果将开放封闭原则应用在上述需求中,那么就不允许在函数 f1 、f2、f3、f4的内部进行修改代码,老板就给了Low BBB一个实现方案:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: antcolonies def w1(func):
def inner():
# 验证1
# 验证2
# 验证3
return func()
return inner @w1
def f1():
print('in the f1') @w1
def f2():
print('in the f2') @w1
def f3():
print('in the f3') @w1
def f4():
print('in the f4')
对于上述代码,也是仅仅对基础平台的代码进行修改,就可以实现在其他人调用函数 f1 f2 f3 f4 之前都进行【验证】操作,并且其他业务部门无需做任何操作。
Low BBB心惊胆战的问了下,这段代码的内部执行原理是什么呢?
老大正要生气,突然Low BBB的手机掉到地上,恰恰屏保就是Low BBB的女友照片,老大一看一紧一抖,喜笑颜开,交定了Low BBB这个朋友。详细的开始讲解了:
以下面为例:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: antcolonies import time def timer(func):
def deco():
start_time = time.time()
func()
stop_time = time.time()
print('the func run time is %s' %(stop_time - start_time))
return deco @timer # test1 = timer(test1)
def test1():
time.sleep(3)
print('in the test1') test1()
下面解析一下以上代码的执行(解释器解释)过程:
1. 解释 import time
2. 初始化 def timer(func): ,即将函数变量timer和形参入栈内存
3. 解释 第5行和第6行代码,因为@timer;def test1(): 是一个整体结构(语法糖),相当于第2步的初始化,解释的结果是: test1 = timer(test1)
4. 执行test1 = timer(test1),注意此时timer(test1)中的test1是def test1(): 中的函数名,存储于栈内存,timer(test1)将函数名test1作为实参传入timer()函数
5. 接着初始化def deco(): 入栈内存,并将函数名deco以返回值的形式赋值给第3步中的test1 = timer(test1),故此时test1的值为函数deco的函数体的内存地址
6. 接着至第20行解析test1(),由于此时test1的值为deco的内存地址,故接下来会调用函数deco
7. 依次执行函数deco的函数体start_time = time.time(),func()
8. 由于第3步中有实参传入,故此时的形参func即为test1, 即执行test1(),调用函数def test1(): 并执行之
9. 接着执行第11和12行。整个程序模块执行完毕。
执行结果:
in the test1
the func run time is 9.18652606010437(debug模式下得出的结果)
先把上述流程看懂,之后还会继续更新...
3、问答时间
问题:当被装饰(或扩充)的函数有参数,而且参数个数不一致时:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: antcolonies import time def timer(func):
def deco(argv1,argv2):
start_time = time.time()
func(argv1,argv2)
stop_time = time.time()
print('the func run time is %s' %(stop_time - start_time))
return deco @timer
def test2(name, age):
time.sleep(2)
print('in the test2: %s %d' %(name, age)) test2('alex',22) '''
in the test2: alex 22
the func run time is 2.000114917755127
'''
有参数
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: antcolonies import time def timer(func):
def deco():
start_time = time.time()
func()
stop_time = time.time()
print('the func run time is %s' %(stop_time - start_time))
return deco @timer # test1 = timer(test1)
def test1():
time.sleep(2)
print('in the test1') @timer
def test2(name, age):
time.sleep(2)
print('in the test2: %s %d' %(name, age)) test1()
test2('alex',22) '''
D:\Python\python.exe E:/python14_workspace/s14/day04/decorator_4_2.py
in the test1
the func run time is 2.0001139640808105
Traceback (most recent call last):
File "E:/python14_workspace/s14/day04/decorator_4_2.py", line 26, in <module>
test2('alex',22)
TypeError: deco() takes 0 positional arguments but 2 were given Process finished with exit code 1
'''
参数不固定
发现当参数不固定时,会出现错误,故需将其改进:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: antcolonies import time def timer(func):
def deco(*argv):
start_time = time.time()
func(*argv)
stop_time = time.time()
print('the func run time is %s' %(stop_time - start_time))
return deco @timer # test1 = timer(test1)
def test1():
time.sleep(2)
print('in the test1') @timer
def test2(name, age):
time.sleep(2)
print('in the test2: %s %d' %(name, age)) test1()
test2('alex',22) '''
in the test1
the func run time is 2.000114917755127
in the test2: alex 22
the func run time is 2.0001139640808105
'''
不固定参数形式_ *args
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: antcolonies import time def timer(func):
def deco(*args,**kwargs):
start_time = time.time()
func(*args,**kwargs)
stop_time = time.time()
print('the func run time is %s' %(stop_time - start_time))
return deco @timer # test1 = timer(test1)
def test1():
time.sleep(2)
print('in the test1') @timer
def test2(name, age):
time.sleep(2)
print('in the test2: %s %d' %(name, age)) test1()
test2('alex',age = 22) # 有关键参数时 '''
in the test1
the func run time is 2.0001139640808105
in the test2: alex 22
the func run time is 2.000114917755127
'''
有关键参数形式_ *args,**kwargs
装饰器应用实例:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: antcolonies usr,passwd = 'alex','abc123' def auth(func):
def wrapper(*args, **kwargs):
username = input("username:").strip()
password = input("password:").strip()
if username == usr and password == passwd:
print('\033[32;1mUser has passed authentication.\033[0m')
return func(*args,**kwargs)
else:
exit('\033[31;1mInvalid username or password.\033[0m')
return wrapper def index():
print('welcome to index page.') @auth
def home():
print('welcome to home page.')
return 'from home' @auth
def bbs():
print('welcome to bbs page.') index()
print(home())
bbs()
论坛
出现以上的当登录home()和bbs模块时,需要通过验证。但是当不同的登录渠道需要不同的验证处理时,以上模块代码就傻眼了。故针对不同的登录渠道需要个性化的验证:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: antcolonies usr,passwd = 'alex','abc123' def auth(auth_type): # 增加个性化定制参数
print('auth func:', auth_type)
def outer_wrapper(func):
def wrapper(*args, **kwargs):
print('auth func args:', *args, **kwargs)
if auth_type == 'local':
username = input("username:").strip()
password = input("password:").strip()
if username == usr and password == passwd:
print('\033[32;1mUser has passed authentication.\033[0m')
return func(*args, **kwargs)
else:
exit('\033[31;1mInvalid username or password.\033[0m')
elif auth_type == 'ldap':
print('get hell out here!')
return wrapper
return outer_wrapper def index():
print('welcome to index page.') @auth(auth_type = 'local') # 个性化定制
def home():
print('welcome to home page.')
return 'from home' @auth(auth_type = 'ldap')
def bbs():
print('welcome to bbs page.') index()
print(home())
bbs()
个性化验证
4、functools.wraps
上述的装饰器虽然已经完成了其应有的功能,即:装饰器内的函数代指了原函数,注意其只是代指而非相等,原函数的元信息没有被赋值到装饰器函数内部。例如:函数的注释信息。
def outer(func):
def inner(*args, **kwargs):
print(inner.__doc__) # None
return func()
return inner @outer
def function():
"""
asdfasd
:return:
"""
print('func')
无元信息
如果使用functools模块中的@functools.wraps装饰的装饰器内的函数,那么就会代指元信息和函数。
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: antcolonies import functools def outer(func):
@functools.wraps(func)
def inner(*args, **kwargs):
print(inner.__doc__)
return func()
return inner @outer
def function():
"""
decorator
asdfasd
:return:
"""
print('func') function() ''' decorator
asdfasd
:return: func
'''
含元信息
Python 装饰器的形成过程的更多相关文章
- 关于python装饰器
关于python装饰器,不是系统的介绍,只是说一下某些问题 1 首先了解变量作用于非常重要 2 其次要了解闭包 def logger(func): def inner(*args, **kwargs) ...
- python装饰器通俗易懂的解释!
1.python装饰器 刚刚接触python的装饰器,简直懵逼了,直接不懂什么意思啊有木有,自己都忘了走了多少遍Debug,查了多少遍资料,猜有点点开始明白了.总结了一下解释得比较好的,通俗易懂的来说 ...
- Python 装饰器学习
Python装饰器学习(九步入门) 这是在Python学习小组上介绍的内容,现学现卖.多练习是好的学习方式. 第一步:最简单的函数,准备附加额外功能 1 2 3 4 5 6 7 8 # -*- c ...
- python 装饰器修改调整函数参数
简单记录一下利用python装饰器来调整函数的方法.现在有个需求:参数line范围为1-16,要求把9-16的范围转化为1-8,即9对应1,10对应2,...,16对应8. 下面是例子: def fo ...
- python 装饰器学习(decorator)
最近看到有个装饰器的例子,没看懂, #!/usr/bin/python class decorator(object): def __init__(self,f): print "initi ...
- Python装饰器详解
python中的装饰器是一个用得非常多的东西,我们可以把一些特定的方法.通用的方法写成一个个装饰器,这就为调用这些方法提供一个非常大的便利,如此提高我们代码的可读性以及简洁性,以及可扩展性. 在学习p ...
- 关于python装饰器(Decorators)最底层理解的一句话
一个decorator只是一个带有一个函数作为参数并返回一个替换函数的闭包. http://www.xxx.com/html/2016/pythonhexinbiancheng_0718/1044.h ...
- Python装饰器由浅入深
装饰器的功能在很多语言中都有,名字也不尽相同,其实它体现的是一种设计模式,强调的是开放封闭原则,更多的用于后期功能升级而不是编写新的代码.装饰器不光能装饰函数,也能装饰其他的对象,比如类,但通常,我们 ...
- Python装饰器与面向切面编程
今天来讨论一下装饰器.装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志.性能测试.事务处理等.装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数 ...
随机推荐
- Windows Touch 便笺簿的
Windows Touch 便笺簿的 C# 示例 (MTScratchpadWMTouchCS) 本节介绍 Windows Touch 便笺簿的 C# 示例. Windows Touch 便笺簿的 ...
- 报错之-Cannot set property 'onclick' of null
当js文件放在head里面时,如果绑定了onclick或者onmouseover事件,就会出现如下图类似的错误,是因为浏览器的加载你写的html文档的顺序是从上往下,加载完按钮节点才执行的js,所以当 ...
- MyEclipse、Eclipse SVN插件的帐号、密码修改
问题描述: Eclipse的SVN插件Subclipse做得很好,在svn操作方面提供了很强大丰富的功能.但到目前为止,该插件对svn用户的概念极为淡薄,不但不能方便地切换用户,而且一旦用户的帐号.密 ...
- 《Kubernetes权威指南第2版》学习(三)RC学习
1 RC文件介绍: kind: ReplicationController,表示是一个RC: spec.selector: RC的Pod标签(Label)选择器,监控和管理拥有这些标签的Pod实例, ...
- 针对nginx的内核优化
关于内核参数的优化: net.ipv4.tcp_max_tw_buckets = 6000timewait的数量,默认是180000.net.ipv4.ip_local_port_range = 10 ...
- 面试题: 数据库 已看1 group by 和order by的练习 sql语句练习简单 有用
1.Sql 约束 http://www.cnblogs.com/henw/archive/2012/08/15/2639510.html 2.修改列类型 MySQL:ALTER TABLE table ...
- Spring入门第五课
集合属性 在Spring中可以通过一组内置的xml标签(如:<list>,<set>,<map>)来配置集合属性. 配置java.util.List类型的属性,需要 ...
- 超级台阶 (NYOJ—76)
很简单的高中数学题,写出来主要是提醒自己,写完递推公式(尤其是公式)一定要检查多遍. #include<stdio.h> #include<string.h> int M; i ...
- 机器学习--K折交叉验证和非负矩阵分解
1.交叉验证 交叉验证(Cross validation),交叉验证用于防止模型过于复杂而引起的过拟合.有时亦称循环估计, 是一种统计学上将数据样本切割成较小子集的实用方法. 于是可以先在一个子集上做 ...
- HTML5与CSS3实例教程(第2版) 附源码 中文pdf扫描版
HTML5和CSS3技术是目前整个网页的基础.<HTML5与CSS3实例教程(第2版)>共分3部分,集中讨论了HTML5和CSS3规范及其技术的使用方法.这一版全面讲解了最新的HTML5和 ...