大聊Python----装饰器
Posted 追风的小蚂蚁
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了大聊Python----装饰器相关的知识,希望对你有一定的参考价值。
什么是装饰器?
装饰器其实和函数没啥区别,都是用def去定义的,其本质就是函数,而功能就是装饰其他的函数,说白了就是为其他函数提供附加功能
装饰器有什么作用?
比如你是一个公司的员工,你所写的程序里有100个函数,但是你所写的程序都已经上线运行了,突然有一天你的产品经理来找你,让你在咱们的APP上新增一段功能!那你说该怎么做这件事情?问题是你的程序都已经在运行了 ,不能修改你程序的源代码,否则会出现意想不到的效果!所以你想新增一项功能,但是不能修改你的源代码!那该怎么办呢?
装饰器对待被修饰的函数是完全透明的状态!也就是函数感觉不到装饰器的存在,装饰器没有动函数的源代码,也不影响函数的运行。
先看一下代码:
import time def timmer(func): # 装饰器 def warpper(*args,**kwargs): start_time = time.time() # 开始的时间 func() stop_time = time.time() # 结束的时间 print(\'the fun run time is %s\'%(stop_time - start_time)) return warpper @timmer # 被装饰的函数 def test1(): time.sleep(3) # 延时3秒 print("in the test1") test1()
结果展示:
实现装饰器知识储备
1、函数即“变量”
机制:
函数调用顺序:其他高级语言类似,Python 不允许在函数未声明之前,对其进行引用或者调用
错误示范:
def foo(): print \'in the foo\' bar() foo() 报错: in the foo Traceback (most recent call last): File "<pyshell#13>", line 1, in <module> foo() File "<pyshell#12>", line 3, in foo bar() NameError: global name \'bar\' is not defined
def foo(): print \'foo\' bar() foo() def bar(): print \'bar\' 报错:NameError: global name \'bar\' is not defined
正确示范:(注意,python为解释执行,函数foo在调用前已经声明了bar和foo,所以bar和foo无顺序之分)
def bar(): print \'in the bar\' def foo(): print \'in the foo\' bar() foo() def foo(): print \'in the foo\' bar() def bar(): print \'in the bar\' foo()
2、高阶函数
a、就是把函数名当做实参传给另外一个函数(在不修改被装饰函数源代码的情况下为其增添功能)
示例:
import time def bar(): time.sleep(3) print("in the bar") def test1(func): start_time = time.time() func() stop_time = time.time() print("the func run rime is %s"%(stop_time - start_time)) test1(bar)
结果:
b、返回值中包含函数名(不修改函数的调用方式)
示例:
import time def bar(): time.sleep(3) print("in the bar") def test2(func): print(func) return func bar = test2(bar) print(bar) # run bar
效果:
<function bar at 0x00000000007C48C8>
<function bar at 0x00000000007C48C8>
3、嵌套函数
局部作用域和全局作用域的访问顺序
x=0 def grandpa(): # x=1 def dad(): x=2 def son(): x=3 print(x) son() dad() grandpa()
显示效果为:
3
高阶函数 + 嵌套函数 ==》 装饰器
先看个例子
import time def timer(func): def deco(): start_time = time.time() func() stop_time = time.time() print("the func\'s run time is %s "%(stop_time - start_time)) return deco @timer def test1(): time.sleep(3) print("the test1 is running!") @timer def test2(): time.sleep(3) print("the test2 is running!") test1() test2()
结果显示:
the test1 is running!
the func\'s run time is 3.000171422958374
the test2 is running!
the func\'s run time is 3.000171661376953
若想给test2传递参数,如下例,该怎么做呢?
import time def timer(func): def deco(): start_time = time.time() func() stop_time = time.time() print("the func\'s run time is %s "%(stop_time - start_time)) return deco @timer def test1(): time.sleep(3) print("the test1 is running!") @timer def test2(name): time.sleep(3) print("the test2 is running!",name) test1() test2("alex")
通过执行,会出现下面的错误
意思是说,deco()缺少了一个元素!
那该怎么解决这个问题呢?
咱们先来捋顺下思路!
通过@timer可知test2() =timer(test2) = deco ,test2(name) = deco(name)
所以可以看出要给deco传递实参,所以做了下面
def timer(func): def deco(name): start_time = time.time() func(name) stop_time = time.time() print("the func\'s run time is %s "%(stop_time - start_time)) return deco
通过执行,会看到下面的结果!
这回好了,test2不出错了,反而test1报错了,那个该怎么办呢?
看下test1出错的原因是test1的deco缺少了一个实参,那么问题来了,test1该怎么处理呢?
其实很简单,使用*args,和**kwargs尽可以完美的解决这个问题!
现在来看看经过改正的程序
import time def timer(func): def deco(*args,**kwargs): start_time = time.time() func(*args,**kwargs) stop_time = time.time() print("the func\'s run time is %s "%(stop_time - start_time)) return deco @timer def test1(): time.sleep(3) print("the test1 is running!") @timer def test2(name): time.sleep(3) print("the test2 is running!",name) test1() test2("alex")
结果显示:
the test1 is running!
the func\'s run time is 3.000171661376953
the test2 is running! alex
the func\'s run time is 3.000171422958374
装饰器之高潮
进入高潮之前,我们先来点前戏
先看下面的代码
import time user , passwd = "sutaoyu" , "sutaoyu01" def auth(func): def wrapper(*args,**kwargs): username = input("Username").strip() password = input("Password").strip() if user == username and passwd == password: print("\\033[32;1mUser has passed authentication\\033[0m") func(*args,**kwargs) else: exit("\\033[31;1mInvalid username and password\\033[0m") return wrapper @auth def index(): print("welcome to index Page!") @auth def home(): print("welcome to index Home!") @auth def bbs(): print("welcome to index BBS!") index() home() bbs()
其输出的结果为:
输入正确时:
输入错误时:
现在当我们把前面的代码稍微改一下,装饰器代码不动,只改变下面两个地方
def index():
print("welcome to index Page!")
return "Page"
print(index())
此时看输出的结果:
会发现无结果并为None,那是因为什么呢?
因为装饰器里的wrapper没有返回值所以,我们给他提供返回值即可!
def auth(func): def wrapper(*args,**kwargs): username = input("Username:").strip() password = input("Password:").strip() if user == username and passwd == password: print("\\033[32;1mUser has passed authentication\\033[0m") return func(*args,**kwargs) else: exit("\\033[31;1mInvalid username and password\\033[0m") return wrapper
运行下程序,看看结果
可以看出,问题已经解决!
现在高潮部分即将来临:
我能不能让我的home在认证的时候用本地的认证,但是bbs认证的时候用远程的ldap?
答案是肯定的!
先看下代码!
import time user , passwd = "123" , "123" def auth(auth_type): print("auth_typr:",auth_type) def outer_wrapper(func): def wrapper(*args,**kwargs): print("wrapper func args:",*args,**kwargs) if auth_type == "local": username = input("Username:").strip() password = input("Password:").strip() if user == username and passwd == password: print("\\033[32;1mUser has passed authentication\\033[0m") func(*args,**kwargs) else: exit("\\033[31;1mInvalid username and password\\033[0m") elif auth_type == "ldap": print("搞毛啊!!!!!") return wrapper return outer_wrapper # @auth def index(): print("welcome to Index page!") return "Page" @auth(auth_type = "local") def home(): print("welcome to Home page!") @auth(auth_type = "ldap") def bbs(): print("welcome to BBS page!") index() home() bbs()
运行的结果为:
可以看出,我们的认真已经成功!
以上是关于大聊Python----装饰器的主要内容,如果未能解决你的问题,请参考以下文章