day4装饰器
Python装饰器
1.必备
def foo():
print(foo)
<function foo at 0x7f62db093f28>
>>> foo
<function foo at 0x7f62db093f28>
foo是函数,函数体;
foo()是执行foo函数
def foo():
print(foo)
foo = lambda x:x+1
foo()将执行lambda表达式,而不再是原来的foo函数,这是因为函数foo被重新定义了。
2.需求来了
初创公司有N个业务部门,1个基础平台部门,基础平台负责提供底层的功能,如:数据库操作、redis调用、监控API等功能。业务部门使用基础功能时,只需调用基础平台提供的功能即可。如下:
##############基础平台提供的功能如下###############
def f1():
print(f1)
def f2():
print(f2)
def f3():
print(f3)
def f4():
print(f4)
############### 业务部门A 调用基础平台提供的功能 ###############
f1()
f2()
f3()
f4()
############### 业务部门B 调用基础平台提供的功能 ###############
f1()
f2()
f3()
f4()
目前公司有条不紊的进行着,但是,以前基础平台的开发人员在写代码的时候没有关注验证相关的问题,即:基础平台提供的功能可以被任何人使用。现在需要对基础平台的功能进行重构,为平台提供的所有功能添加验证机制,即:执行功能前,先进行验证。
老大把工作交给了Low B,他是这么做的:
跟每个业务部门交涉,每个业务部门自己写代码,调用基础平台功能之前先验证,艾,这样一来,基础平台就不需要进行任何修改了。
不幸的是,当天Low B就被开除了....
老大把工作交给了Low BB,他是这么做的:
只对基础平台的代码进行重构,让N个业务部门无需做任何修改
##############基础平台提供的功能如下###############
def f1():
#验证1
#验证2
#验证3
print(f1)
def f2():
#验证1
#验证2
#验证3
print(f2)
def f3():
#验证1
#验证2
#验证3
print(f3)
def f4():
#验证1
#验证2
#验证3
print(f4)
############### 业务部门A 调用基础平台提供的功能 ###############
f1()
f2()
f3()
f4()
############### 业务部门B 调用基础平台提供的功能 ###############
f1()
f2()
f3()
f4()
############### 业务部门调用方法不变,不影响业务部门 ###############
过了一周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)
老大看了一下Low BBB的实现,嘴角露出了一丝欣慰的笑,语重心长的跟Low BBB聊了个天:
老大说:
写代码要遵循开放封闭的原则,虽然在这个原则是用在面向对象开发,但是也是用与函数式编程,它规定已经实现的功能代码不允许被修改,但可以被扩展,即:
(1)封闭:以实现的功能代码块
(2)开放:对扩展开发,可以开发新功能,但尽量不要修改原代码。
如果将开放封闭原则应用在上述需求中,那么就不允许在函数f1、f2、f3、f4的内部修改代码,老大就被给了Low BBB一个实现方案:
def w1(func):
def inner():
#验证1
#验证2
验证3
return func()
return inner
@w1
def f1():
print(f1)
@w1
def f2():
print(f2)
@w1
def f3():
print(f3)
@w1
def f4():
print(f4)
############### 业务部门调用方法不变,不影响业务部门 ###############
对于上述代码,也是仅仅对基础平台的代码进行了修改,就可以实现在其他人调用函数f1,f2,f3,f4之前都进行[验证]的操作,并且其他部门无需做任何修改。
Low BBB心惊胆战的问了下,这段代码的内部执行原理是什么呢?
老大正要生气,突然Low BBB的手机掉到了地上,恰巧屏保就是Low BBB的女朋友照片,老大一看一紧一抖,嘻笑眼开,交定了Low BBB这个朋友。详细的讲解开始了:
单独以f1为例:
def w1(func):
def inner():
#验证1
#验证2
#验证3
return func()
return inner
@w1
def f1():
print(f1)
当写完这段代码后(函数未被执行、未被执行、未被执行),Python解释器就会从上到下解释代码,步骤如下:
(1)def w1(func): ==>将w1加载到内存,扫描加载到内存。
(2)@w1
没错,从表面上看解释器仅仅会解释这两句代码,因为函数在没有被调用之前其内部代码不会被执行。
从表面上看解释器着实会执行这两句,但是@w1这一句代码却大有文章,@函数名是Python的一种语法糖(装饰器)。
如上例@w1内部会执行以下操作:
(1)执行w1函数,并将@wq下面的函数作为w1函数的参数,即:@w1等价于w1(f1)
所以,内部就会去执行:
def inner():
#验证
return f1() #func是参数,此时func等价于f1
return inner #返回的inner,inner代表的是函数,非执行函数
其实就是将原来的f1函数塞进另外以一个函数中
(2)将执行玩的w1函数返回值赋值给@w1下面的函数的函数名
w1函数的返回值是:
def inner():
#验证
return f1() #此处表示原来的f1函数
然后,将此返回值在重新赋值给f1,即:
新f1 = def inner:
#验证
return f1()
所以,以后业务部门想要执行f1函数时,就会执行新f1函数,在新f1函数内部先执行验证,再执行原来的f1函数,然后将原来f1函数的返回值返回给了业务调用者。
Low BBB你明白了吗?要是没有明白的话,我晚上去你家帮你解决吧!!!
3.问答时间
问题:被装饰的函数有参数呢?
一个参数:
def w1(func):
def inner(arg):
#验证1
#验证2
#验证3
return func(arg)
return inner
@w1
def f1(arg):
print(f1)
二个参数:
def w1(func):
def inner(arg1,arg2)
#验证1
#验证2
#验证3
return func(arg1,arg2)
return inner
@w1
def f1(arg1,arg2):
print(f1)
三个参数:
def w1(func):
def inner(arg1,arg2,arg3):
#验证1
#验证2
#验证3
return func(arg1,arg2,arg3)
return inner
@w1
def f1(arg1,arg2,arg3):
print(f1)
问题:可以装饰具有处理n个参数的函数的装饰器?
def w1(func):
def inner(*args,**kwargs):
#验证1
#验证2
#验证3
return func(*args,**kwargs)
return inner
@w1
def f1(arg1,arg2,arg3):
print(f1)
问题:一个函数可以被多个装饰器装饰码?
def w1(func):
def inner(*args,**kwargs):
#验证1
#验证2
#验证3
return func(*args,**kwargs)
return inner
def w2(func):
def inner(*args,**kwargs):
#验证1
#验证2
#验证3
return func(*args,**kwargs)
return inner
@w1
@w2
def f1(arg1,arg2,arg3):
print(f1)
下面有一个例子,需要我们来完成,我们需要设置一个网页后台,当用户登录非主页的时候需要验证,如何实现呢?
首先,我们先定义好各个模块:
def login(func):
#登录验证模块
print("passed user verification......")
return func
def home(name):
#主页,不需要登录验证
print("Welcome [%s] to home page." %name)
def tv(name):
#TV页面,进来需要验证登录
print("Welcome [%s] to TV page." %name)
def movie(name):
#movie页面,进来需要登录验证
print("Welcome [%s] to movie page." %name)
上面代码,我们实现了几个函数体,然后,我们知道,要想让用户进行验证,那么在进入网页的时候要先执行login函数,如何执行函数呢,必须调用函数,调用函数之后有一个返回值,我们知道,要不修改原代码以及方便用户操作,用户只需要输入tv(name)即可,这样就直接调用的是tv模块,因为。我们知道,程序是按照串行的方式执行的。那么,我们可以事先执行login()模块,然后返回一个值。在执行tv函数。
def login(func):
#登录验证模块
print("passed user verification......")
return func
def home(name):
print("Welcome [%s] to home page." %name)
def tv(name):
print("Welcome [%s] to TV page." %name)
def movie(name):
print("Welcome [%s] to movie page." %name)
tv = login(tv)
#把tv当作参数传递给login函数,然后返回tv参数,目的就是为了执行一次验证
tv("alex")
运行结果如下:
passed user verification......
Welcome [alex] to TV page.
虽然我们完美执行了这个程序。但是这个程序有问题,因为用户调用的时候输入的是tv("alex"),那么在没有调用的时候运行程序会是怎样的:
def login(func):
#登录验证模块
print("passed user verification......")
return func
def home(name):
print("Welcome [%s] to home page." %name)
def tv(name):
print("Welcome [%s] to TV page." %name)
def movie(name):
print("Welcome [%s] to movie page." %name)
tv = login(tv)
#把tv当作参数传递给login函数,然后返回tv参数
#tv("alex")
运行结果如下:
passed user verification......
此时,我们并没有调用主页,但是也提示让用户进行输入验证,这样就没有太多实际意义了,因此要想办法让用户没有调用的时候什么都不打印。
def login(func):
#登录验证模块
print("passed user verification......")
return func
def home(name):
print("Welcome [%s] to home page." %name)
@login
def tv(name):
print("Welcome [%s] to TV page." %name)
def movie(name):
print("Welcome [%s] to movie page." %name)
#tv = login(tv)
#把tv当作参数传递给login函数,然后返回tv参数
#tv("alex")
#login相当于tv = login(tv)当程序没有调用的时候执行也返回让用户调用,显然是不合理的。这样没有执行调用就需要验证显然是不合理的,要想办法让程序没有调用的时候不要指定调用。
def login(func):
#登录验证模块
def inner(name):
#设置让用户调用的时候执行,否则不执行,避免没有调用就执行。
print("passed user verification......")
func(name)
return inner
def home(name):
print("Welcome [%s] to home page." %name)
def tv(name):
print("Welcome [%s] to TV page." %name)
def movie(name):
print("Welcome [%s] to movie page." %name)
tv = login(tv)
#把tv当作参数传递给login函数,然后返回tv参数
#tv("alex")
在上面程序中,我们在验证模块嵌套了一层函数,用于让用户在没有调用的情况下,执行程序不会执行验证模块,为什么可以呢?我们知道,执行函数需要函数名function()加上括号才能够执行,因此我们在这行循环的时候返回内存函数名,然后进行调用,这样就能够在用户验证的时候执行验证模块,而在用户没有调用的过程中,返回函数名,但是这样时候因为只有函数名,因而是不能够执行函数的。
下面来看看用装饰器实现的情况:
def login(func):
#登录验证模块
def inner(name):
#设置让用户调用的时候执行,否则不执行,避免没有调用就执行。
print("passed user verification......")
func(name)
return inner
def home(name):
print("Welcome [%s] to home page." %name)
@login
def tv(name):
print("Welcome [%s] to TV page." %name)
@login
def movie(name):
print("Welcome [%s] to movie page." %name)
#tv = login(tv)
#把tv当作参数传递给login函数,然后返回tv参数
tv("alex")
movie("tom")
上面代码中,我们使用了装饰器,让程序在运行的过程中首先使用了装饰器,装饰器相当于tv = login(tv)让第一次函数返回的值赋值给tv,这样在用户调用tv函数的时候,其实并没有执行tv函数的代码,而是执行内存函数的代码,就直接调用第二次验证模块,验证完成之后,我们指定用户打印模块。这样我们就避免了在用户还没有调用的时候就执行了验证模块。
day4装饰器的更多相关文章
- 小白的Python之路 day4 装饰器前奏
装饰器前奏: 一.定义: 1.装饰器本质是函数,语法都是用def去定义的 (函数的目的:他需要完成特定的功能) 2.装饰器的功能:就是装饰其他函数(就是为其他函数添加附加功能) 二.原则: 1. 不能 ...
- 小白的Python之路 day4 装饰器高潮
首先装饰器实现的条件: 高阶函数+嵌套函数 =>装饰器 1.首先,我们先定义一个高级函数,去装饰test1函数,得不到我们想要的操作方式 import time #定义高阶函数 def deco ...
- Day4 装饰器——迭代器——生成器
一 装饰器 1.1 函数对象 一 函数是第一类对象,即函数可以当作数据传递 #1 可以被引用 #2 可以当作参数传递 #3 返回值可以是函数 #3 可以当作容器类型的元素 二 利用该特性,优雅的取代多 ...
- day4 装饰器深入解析
Python装饰器 装饰器是在不修改源码给代码添加功能的常用方法.@是装饰的标志.我们知道,在给代码增加功能的时候,要遵循开放封闭的原则,不能随便更改原码,因此装饰器的功能就显示出来了,只需要在函数前 ...
- day4装饰器-迭代器&&生成器
一.装饰器 定义:本质是函数,(装饰其他函数)就是为其它函数添加附加功能 原则:1.不能修改被装饰的函数的源代码 2.不能修改被装饰的函数的调用方式 实现装饰器知识储备: 1.函数及“变量” 2.高阶 ...
- 装饰器、生成器,迭代器、Json & pickle 数据序列化
1. 列表生成器:代码例子 a=[i*2 for i in range(10)] print(a) 运行效果如下: D:\python35\python.exe D:/python培训/s14/day ...
- Python之旅Day5 列表生成式 生成器 迭代器 装饰器
装饰器 器即函数,装饰即修饰,意指为其他函数添加新功能 装饰器定义:本质就是函数,功能是为其他函数添加新功能 装饰器涉及的知识点= 高阶函数+函数嵌套+闭包 在遵循下面两个原则的前提下为被装饰者新功能 ...
- 函数和常用模块【day05】:装饰器高潮(三)
本节内容 1.概述 2.装饰器定义 3.装饰器定义 4.带参数的生成器 一.概述 我们之前介绍了大幅片的内容,感觉跟装饰器半毛钱关系都没有,其实不然,我们分别详细阐述了高阶函数和内置函数,下面我们就来 ...
- 函数和常用模块【day05】:装饰器前奏(一)
本节内容 定义 原则 实现装饰器的储备知识 函数及变量 高阶函数 一.定义 1.装饰器:本质是函数. 2.功能:用来装饰其他函数,顾名思义就是,为其他的函数添加附件功能的. 二.原则 不能修改被装饰函 ...
随机推荐
- Nginx报错 nginx: [error] open() "/usr/local/nginx-1.6.3/logs/nginx.pid" failed (2: No such file or directory)
问题: 解决: http://www.jianshu.com/p/918eb337a206 dd
- C++ 局部静态变量,全局变量,全局静态变量,局部变量的区别和联系
C++变量根据定义位置的不同,具有不同的作用域,作用域可分为6种:全局作用域,局部作用域,语句作用域,类作用域,命名作用域和文件作用域. 从作用域看: 全局变量具有全局作用域.全局变量只需在一个源文件 ...
- 【转】虚拟机克隆之后,网卡名称从eth0变成eth1之后的解决办法
使用VMware安装了CentOS虚拟机,克隆之后使用service network restart指令来重新启动网络服务时,会看到有eth0网卡不存在的提示. 出现这种现象的原因是,很多Linu ...
- python基础--文件操作实现全文或单行替换
python修改文件时,使用w模式会将原本的文件清空/覆盖.可以先用读(r)的方式打开,写到内存中,然后再用写(w)的方式打开. 替换文本中的taste 为 tasting Yesterday whe ...
- SQL SERVER2008不在同一局域网内实现订阅发布的方法
我们知道,在新建订阅的时候,必须要使用实际服务器的真实名称才可以,IP地址,别名都不可以,在同一个局域网内是没有问题的.但是两台服务器不在同一个局域网内就不行了 要想实现订阅和发布,本人用到的最简单的 ...
- vue 和react
React 和 Vue 有许多相似之处,它们都有: 使用 Virtual DOM 提供了响应式 (Reactive) 和组件化 (Composable) 的视图组件. 将注意力集中保持在核心库,而将其 ...
- .net core 中 identity server 4 之术语
id4的职责: 保护你的资源 通过本地 账户库(Account Store)或者外部身份提供其 认证用户 提供Session管理以及SSO 管理和认证客户端 发行身份及访问Token给客户端 验证To ...
- 谈谈对Spring IOC(控制反转)的理解--转
谈谈对Spring IOC(控制反转)的理解--转 学习过Spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC ...
- 【BZOJ】1690: [Usaco2007 Dec]奶牛的旅行
[算法]01分数规划-最优比率环 [题意]给定有向图,点有收益,边有代价,重复经过的话收益不叠加而代价叠加,求从任意点开始最后回归该点的(收益/代价)最大. [题解] 和普通的分数规划不同,这里的方案 ...
- 【计蒜客】是男人就过 8 题--Pony.AI 题 A. A String Game 后缀自动机+SG函数
[题目]A. A String Game [题意]给定目标串S和n个子串Ti,Alice和Bob轮流选择一个子串操作,必须且只能在子串末尾添加一个字符使得新串也是S的子串,不能操作即输,求胜利者.|S ...