装饰器教程

无参装饰器的一般形式 a、 解决原生函数有参问题 b、解决原生函数有返回值问题

无参装饰器的模型总结 无参装饰器的具体应用实例 有参装饰器 有参装饰器的具体应用实例 对于大部分学Python的人来说,装饰器可能是遇到的第一个坎,装饰器到底是什么,到底应该怎么用?本篇博客将进行彻底的讲解。

装饰器的概念:

1、装饰器就是为了在不修改被装饰对象的源代码以及调用方式的前提下,为其添加新的功能;

2、装饰器本身可以是任何可调用的对象,被装饰的对象也可以是任意可调用的对象;

简单来说装饰器就是修改别人的工具,其中修饰指的是添加功能,工具指的是函数。

装饰器的语法:

假设被装饰的函数是index,请写出@timer的含义:

index = timer(index)

场景:假如我现在有3个函数,如下所示:

def index(): print('欢迎来到python世界')

def home(): print('欢迎来到scala世界')

def edit(): print('欢迎来到Java世界')

index() home() edit() 现在的需求:求出每一个函数的运行时间:

渣渣aa的做法:

import time

def index(): start_time = time.time() print('欢迎来到python世界') end_time = time.time() print('run time is %s'%(end_time-start_time))

def home(): start_time = time.time() print('欢迎来到scala世界') end_time = time.time() print('run time is %s'%(end_time-start_time))

def edit(): start_time = time.time() print('欢迎来到Java世界') end_time = time.time() print('run time is %s'%(end_time-start_time))

index() home() edit() 渣渣bb的做法:

def decorator(func): start_time = time.time() func() end_time = time.time() print('run time is %s'%(end_time-start_time))

def index(): print('欢迎来到python世界')

def home(): print('欢迎来到scala世界')

def edit(): print('欢迎来到Java世界')

decorator(index) decorator(home) decorator(edit) 其中渣渣a的问题:修改了源代码;渣渣b的问题:修改了函数正常的调用方式。

问题产生了:我们如何在不修改被装饰对象的源代码以及调用方式的前提下为函数增加新的功能呢?这个时候我们的装饰器就上场了。

装饰器的解决方案:

import time

def decorator(func): def wrapper(): start_time = time.time() func() end_time = time.time() print('run time is %s' % (end_time - start_time)) return wrapper

@decorator def index(): print('欢迎来到python世界')

@decorator def home(): print('欢迎来到scala世界')

@decorator def edit(): print('欢迎来到Java世界')

index() home() edit()   运行结果:

View Code

对于上面的代码,第一眼看上去是不是感觉很蒙圈,其实那是因为我中间省略了一个重要的步骤:

!/usr/bin/python

-- coding:utf-8 --

import time

def decorator(func): def wrapper(): start_time = time.time() func() end_time = time.time() print('run time is %s' % (end_time - start_time)) return wrapper

def index(): print('欢迎来到python世界') index = decorator(index) print(index,' ',index.name)

def home(): print('欢迎来到scala世界') home = decorator(home)

def edit(): print('欢迎来到Java世界') edit = decorator(edit)

index() home() edit() 随后在上一张图片:当执行完 index = decorator(index) 之后:返回的wrapper实际上是一个指针变量,所以index和wrapper将指向同一块内存空间,也就是说index已经不是我们之前的index了,而是悄悄的变成了闭包函数wrapper,wrapper在控制着index。

我想你也许发现哪里发生变化了,即index = decorato(index)等价于@decorator,对的,这就是装饰器的本质:Python的一种语法糖而已,装饰器就是闭包函数的一种实现。

上面我们使用的是简单的无参装饰器,但是对于上面的程序实际上还是有两个缺陷的:

假如3个原生函数是这样的:

View Code

如果我们还按照上面的方式去运行代码,将会抛出错误,因为当执行到func()的时候,实际上执行的是原始的index函数,但是我们在调用的时候并没有传入参数,所以:解决bug1:原生函数带有参数

import time

def decorator(func): def wrapper(args,kwargs): #wrapper('小泡芙') start_time = time.time() func(args,**kwargs) #执行到这里的时候会发现少了一个参数,因为原始的index函数必须传入一个参数 end_time = time.time() print('run time is %s' % (end_time - start_time)) return wrapper

@decorator def index(name): print('欢迎来到python世界')

@decorator def home(): print('欢迎来到scala世界')

@decorator def edit(): print('欢迎来到Java世界')

index('小泡芙') home() edit() 但是接下来问题又来了,如果原生函数带有返回值我们怎么获取呢?

@decorator def index(name): print('欢迎来到python世界') return 'Hello World'

@decorator def home(): print('欢迎来到scala世界')

@decorator def edit(): print('欢迎来到Java世界')

print(index('小泡芙')) home() edit() 如果我们还按照以前的方式的话,你会发现:返回值将是None,为什么呢?因为wrapper函数的返回值就是None。

解决bug2:原生函数带有返回值

!/usr/bin/python

-- coding:utf-8 --

import time

def decorator(func): def wrapper(args,kwargs): #wrapper('小泡芙') start_time = time.time() res = func(args,**kwargs) #执行到这里的时候会发现少了一个参数,因为原始的index函数必须传入一个参数 end_time = time.time() print('run time is %s' % (end_time - start_time)) return res return wrapper

@decorator def index(name): print('欢迎来到python世界') return 'Hello World'

@decorator def home(): print('欢迎来到scala世界')

@decorator def edit(): print('欢迎来到Java世界')

print(index('小泡芙')) home() edit() 运行结果:

欢迎来到python世界 run time is 0.0 Hello World 欢迎来到scala世界 run time is 0.0 欢迎来到Java世界 run time is 0.0

Process finished with exit code 0 到这里我们将引出常用无参装饰器的模型:

def decorator(func): def wrapper(args,kwargs): res = func(args,**kwargs) return res return wrapper 接下来我们举一个具体的实例场景来说明装饰器的具体应用:要求函数在执行真正的代码之前,先实现一段认证功能,只有认证通过了,

才可以执行真正的功能。(该场景实际上是后期Django的cookie和session的应用)

!/usr/bin/python

-- coding:utf-8 --

import time

def decorator(func): def wrapper(args,kwargs): name = input('请输入用户名:') pwd = input('请输入密码:') if name == 'eric' and pwd == '123456': print('\033[42m登陆成功....\033[0m') res = func(args,**kwargs) return res else: print('\033[42m登陆失败,您的用户名或者密码输入有误..\033[0m') return wrapper

@decorator def index(): print('欢迎来到登陆页面')

@decorator def home(): print('欢迎来到用户信息界面')

@decorator def edit(): print('欢迎来到编辑界面')

index() home() edit() 运行结果:

请输入用户名:eric 请输入密码:123456 登陆成功.... 欢迎来到登陆页面 请输入用户名:Jack 请输入密码:12346 登陆失败,您的用户名或者密码输入有误.. 请输入用户名:Angela 请输入密码:123 登陆失败,您的用户名或者密码输入有误.. 但是上面的这个程序实际上还是存在问题,什么问题呢?这个问题就好比我已经登陆了京东的网页页面,但是后续无论我访问什么页面都需要再次登录一下登陆界面,这个问题确实有点扯。

解决方法:这个问题的真实场景是通过cookie或者session来记录用户登录状态的,但是在这里我们只能暂时通过全局变量来模拟这种效果了。

!/usr/bin/python

-- coding:utf-8 --

import time

user_info = {'user':None,'status':False}

def decorator(func): def wrapper(args,kwargs): if user_info['user'] and user_info['status']: res = func(args, **kwargs) return res else: name = input('请输入用户名:') pwd = input('请输入密码:') if name == 'eric' and pwd == '123456': print('\033[42m登陆成功....\033[0m') # 用户一旦登陆成功,我们就将登陆成功的信息记录下来 user_info['user'] = 'eric' user_info['status'] = True

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n90" mdtype="fences" style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0); font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-size: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); border: 1px solid rgb(231, 234, 237); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; position: relative !important;"> res = func(args,*kwargs)
return res
else:
print('\033[42m登陆失败,您的用户名或者密码输入有误..\033[0m')
return wrapper</pre>

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n107" mdtype="fences" style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0); font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-size: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); border: 1px solid rgb(231, 234, 237); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; position: relative !important;"> res = func(args,kwargs)
return res
else:
print('\033[42m登陆失败,您的用户名或者密码输入有误..\033[0m')
elif auth == 'dba':
print('dba的验证方式')
if user_info['user'] and user_info['status']:
res = func(
args, kwargs)
return res
else:
name = input('请输入用户名:')
pwd = input('请输入密码:')
if name == 'eric' and pwd == '123456':
print('\033[42m登陆成功....\033[0m')
# 用户一旦登陆成功,我们就将登陆成功的信息记录下来
user_info['user'] = 'Eric'
user_info['status'] = True

res = func(
args,
kwargs)
return res
else:
print('\033[42m登陆失败,您的用户名或者密码输入有误..\033[0m')
else:
print('其余的验证方式')
if user_info['user'] and user_info['status']:
res = func(
args, kwargs)
return res
else:
name = input('请输入用户名:')
pwd = input('请输入密码:')
if name == 'eric' and pwd == '123456':
print('\033[42m登陆成功....\033[0m')
# 用户一旦登陆成功,我们就将登陆成功的信息记录下来
user_info['user'] = 'Eric'
user_info['status'] = True

res = func(
args,
*kwargs)
return res
else:
print('\033[42m登陆失败,您的用户名或者密码输入有误..\033[0m')
return wrapper
return decorator</pre>

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n121" mdtype="fences" style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0); font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-size: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); border: 1px solid rgb(231, 234, 237); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; position: relative !important;"> res = func(args,kwargs)
return res
else:
print('\033[42m登陆失败,您的用户名或者密码输入有误..\033[0m')
elif auth == 'dba':
print('dba的验证方式')
if user_info['user'] and user_info['status']:
res = func(
args, kwargs)
return res
else:
name = input('请输入用户名:')
pwd = input('请输入密码:')
if name == 'eric' and pwd == '123456':
print('\033[42m登陆成功....\033[0m')
# 用户一旦登陆成功,我们就将登陆成功的信息记录下来
user_info['user'] = 'Eric'
user_info['status'] = True

res = func(
args,
kwargs)
return res
else:
print('\033[42m登陆失败,您的用户名或者密码输入有误..\033[0m')
else:
print('其余的验证方式')
if user_info['user'] and user_info['status']:
res = func(
args, kwargs)
return res
else:
name = input('请输入用户名:')
pwd = input('请输入密码:')
if name == 'eric' and pwd == '123456':
print('\033[42m登陆成功....\033[0m')
# 用户一旦登陆成功,我们就将登陆成功的信息记录下来
user_info['user'] = 'Eric'
user_info['status'] = True

res = func(
args,
*kwargs)
return res
else:
print('\033[42m登陆失败,您的用户名或者密码输入有误..\033[0m')
return wrapper
return decorator</pre>

———————————————— 版权声明:本文为CSDN博主「安静的技术控」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/a2011480169/article/details/83572838

OK,如有问题,欢迎留言指正。

实际上就是在最开始多了一个步骤,后面的步骤和我们上面是一模一样的。

@outer(auth='file') ===> @decorator ==> index=decorator(index),也就是说,有参装饰器又给内部提供了一个参数。

index() home() edit() 对于有参装饰器,我们注意两点就够了:

@outer(auth='else') def edit(): print('欢迎来到编辑界面')

@outer(auth='dba') def home(): print('欢迎来到用户信息界面')

@outer(auth='file') def index(): print('欢迎来到登陆页面')

def outer(auth='file'): def decorator(func): def wrapper(args,kwargs): if auth == 'file': print('file的验证方式') if user_info['user'] and user_info['status']: res = func(args, **kwargs) return res else: name = input('请输入用户名:') pwd = input('请输入密码:') if name == 'eric' and pwd == '123456': print('\033[42m登陆成功....\033[0m') # 用户一旦登陆成功,我们就将登陆成功的信息记录下来 user_info['user'] = 'eric' user_info['status'] = True

user_info = {'user':None,'status':False}

import time

-- coding:utf-8 --

!/usr/bin/python

index() home() edit() 我们写成装饰器的形式:

edit本质上还是wrapper print(edit,edit.name)

def edit(): print('欢迎来到编辑界面') decorator = outer(auth='dba') edit = decorator(edit)

home本质上还是wrapper print(home,home.name)

def home(): print('欢迎来到用户信息界面') decorator = outer(auth='else') home = decorator(home)

index本质上还是wrapper print(index,index.name)

def index(): print('欢迎来到登陆页面') decorator = outer(auth='file') index = decorator(index)

def outer(auth='file'): def decorator(func): def wrapper(args,kwargs): if auth == 'file': print('file的验证方式') if user_info['user'] and user_info['status']: res = func(args, **kwargs) return res else: name = input('请输入用户名:') pwd = input('请输入密码:') if name == 'eric' and pwd == '123456': print('\033[42m登陆成功....\033[0m') # 用户一旦登陆成功,我们就将登陆成功的信息记录下来 user_info['user'] = 'eric' user_info['status'] = True

user_info = {'user':None,'status':False}

import time

-- coding:utf-8 --

!/usr/bin/python

代码示例:

以上面的场景为例:用户认证的方式包括很多,如文件认证、数据库认证等等,如果利用有参装饰器进行实现呢?

def outer(driver='file'): def decorator(func): def wrapper(*args, *kwargs): res = func(args, **kwargs) return res return wrapper return decorator 其实有参装饰器也没有想象中的那么难,本质上也是闭包函数和无参装饰器的扩展。

有参装饰器的模型:

上面的无参装饰器我们讲完了,接下来我们来谈论有参装饰器的概念:

Process finished with exit code 0

请输入用户名:eric 请输入密码:123456 登陆成功.... 欢迎来到登陆页面 欢迎来到用户信息界面 欢迎来到编辑界面

index() home() edit() 接下来运行结果就正常了:

@decorator def edit(): print('欢迎来到编辑界面')

@decorator def home(): print('欢迎来到用户信息界面')

@decorator def index(): print('欢迎来到登陆页面')

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,616评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,020评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,078评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,040评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,154评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,265评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,298评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,072评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,491评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,795评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,970评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,654评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,272评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,985评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,815评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,852评论 2 351

推荐阅读更多精彩内容

  • 有时候自己真的很奇怪,白天已经累成了狗,晚上依然不想睡觉,比如昨天,要么躺不下去,要么躺下去闭不上眼,直到凌晨才睡...
    云游先生阅读 276评论 0 0
  • 无须死记硬背,直接 copy 就好。 1. 查看目前 mysql 用户 2. 修改 root 密码(使用内置函数修...
    老衲呢阅读 136评论 0 2
  • 对于高晓松我一直没有道理的喜欢,喜欢听他摇着扇子胡天海地的侃大山,看起来信马由缰,细细品味逻辑严密,丝丝入...
    懒羊羊的菜园子阅读 397评论 1 1