Python---装饰器详解

定义:

本质上是一个函数。作用是用来装饰另一个函数(即被装饰函数),给被装饰函数添加功能。前提是不能改变被装饰函数的源代码和调用方式。这样的一个函数称之为装饰器。

解析:

下面我们话不多说,直接用代码说明。下面是一个函数。

1 def add():
2     b=1+2
3     print(b)
4
5 add()
程序输出:
————————

3

————————
  • 现在我要给这个函数增加一个解释性的句子,如下,我们可以编写一个装饰器:
#原函数
def add():
    a=1+2
    print(a)  
#装饰器
def decorator(func):
    def warpper():
        print("1+2的结果是:")
        func()
    return warpper
#注意此句   
add=decorator(add)
#调用函数
add()

程序输出:

——————————

1+2的结果是:

3

——————————
  • 这样我们就成功的达成了我们的目的。这里要注意第12行的这一句,这一句是将add这个函数对象传入了decorator()函数,返回的是一个新函数变量,这个新函数对象又重新赋值给add,这样就可以保证不改变被装饰函数的调用方式不变。在Python语法中有一种更优雅的方式可以代替第十二行的语句。如下:
#装饰器
def decorator(func):
    def warpper():
        print("1+2的结果是:")
        func()
    return warpper

#add=decorator(add)
#原函数
@decorator#换成@符号
def add():
    a=1+2
    print(a)
#调用函数
add()

在被装饰函数前面直接加上“@xxx”(xxx为装饰器函数名)即可

被装饰函数有参数怎么办?

  • 如果被装饰器函数有参数呢?该怎们班?不用担心,我们可以用不定参数的形式来收集参数。实例代码如下:
def decorator(func):
    def warpper(*args,**kwargs):
        print("相加的结果是:")
        func(*args,**kwargs)
    return warpper

@decorator
def add(x,y):
    a=x+y
    print(a)

add(2,3)

程序输出:

——————————————————
相加的结果是:
5
——————————————————

如上,我们给包装函数加上接收参数,然后传给func()函数就行了。这样不管被装饰函数有怎样的参数都不怕了。

下面写一个页面验证的装饰器。

  • 大家知道有些网站的一部分页面是要求用户登录之后才可以访问的,比如下面的三个函数(分别代表三个页面):
def index():
    print("welcome to the index page")
def home():
    print("welcome to the home page")
def bbs():
    print("welcome to the bbs page")
    return "I am the return contents"
  • 假如说现在我们要给home页面和bbs页面加上验证,显然现在更改源代码是不可行的。这个时候我们可以用装饰器,如下:
username,passwd="jack","abc123"#模拟一个已登录用户
def decorator(func):
    def warpper(*args,**kwargs):
        Username=input("Username:").strip()
        password=input("Password:").strip()
        if username==Username and passwd==password:
            print("Authenticate Success!")
            func(*args,**kwargs)
        else:
            exit("Username or password is invalid!")
    return warpper

def index():
    print("welcome to the index page")
@decorator
def home():
    print("welcome to the home page")
@decorator
def bbs():
    print("welcome to the bbs page")
    return "I am the return contents"

index()
home()
bbs()

程序结果:

————————

welcome to the index page    #index页面未验证直接可以登入
Username:jack
Password:abc123
Authenticate Success!           #登录的而情形
welcome to the home page
Username:jack                      #密码或用户名错误的情形
Password:123
Username or password is invalid!
————————
  • 我们注意到bbs()是有返回值的,如果我们把上述代码的最后一句(第25行)改为“print(bbs())”之后再看看他的输出结果:
————————

welcome to the index page
Username:jack
Password:abc123
Authenticate Success!
welcome to the home page
Username:jack
Password:abc123
Authenticate Success!
welcome to the bbs page
None                              #返回值能么成None了???

————————
  • What happened! bbs()的返回值打印出来竟然是None。怎么会这样?这样的话不就改变了被装饰函数的源代码了吗?怎样才能解决呢?

    • 我们来分析一下:

    • 我们执行bbs函数其实就相当于执行了装饰器里的wrapper函数,仔细分析装饰器发现wrapper函数却没有返回值,所以为了让他可以正确保证被装饰函数的返回值可以正确返回,那么需要对装饰器进行修改:

username,passwd="jack","abc123"#模拟一个已登录用户
def decorator(func):
    def warpper(*args,**kwargs):
        Username=input("Username:").strip()
        password=input("Password:").strip()
        if username==Username and passwd==password:
            print("Authenticate Success!")
           return func(*args,**kwargs)#在这里加一个return就行了
        else:
            exit("Username or password is invalid!")
    return warpper

def index():
    print("welcome to the index page")
@decorator
def home():
    print("welcome to the home page")
@decorator
def bbs():
    print("welcome to the bbs page")
    return "I am the return contents"

index()
home()
bbs()
  • 如图加上return就可以解决了。下面我们在看看改后的程序输出:
————————

welcome to the index page
Username:jack
Password:abc123
Authenticate Success!
welcome to the home page
Username:jack
Password:abc123
Authenticate Success!
welcome to the bbs page
I am the return contents   #bbs()的返回值得到了正确的返回

——-——————

好了,返回值的问题解决了.

既然装饰器是一个函数,那装饰器可以有参数吗?

  • 答案是肯定的。我们同样可以给装饰器加上参数。比如还是上面的三个页面函数作为例子,我们可以根据不同页面的验证方式来给程序不同的验证,而这个验证方式可以以装饰器的参数传入,这样我们就得在装饰器上在嵌套一层函数 了:
username,passwd="jack","abc123"#模拟一个已登录用户
def decorator(auth_type):
    def out_warpper(func):
        def warpper(*args,**kwargs):
            Username=input("Username:").strip()
            password=input("Password:").strip()
            if auth_type=="local":
                if username==Username and passwd==password:
                    print("Authenticate Success!")
                    return func(*args,**kwargs)
                else:
                    exit("Username or password is invalid!")
            elif auth_type=="unlocal":
                print("HERE IS UNLOCAL AUTHENTICATE WAYS")
        return warpper
    return out_warpper

def index():
    print("welcome to the index page")
@decorator(auth_type="local")
def home():
    print("welcome to the home page")
@decorator(auth_type="unlocal")
def bbs():
    print("welcome to the bbs page")
    return "I am the return contents"

index()
home()
bbs()

输出:

————————

welcome to the index page
Username:jack
Password:abc123
Authenticate Success!
welcome to the home page
Username:jack
Password:abc123
HERE IS UNLOCAL AUTHENTICATE WAYS

————————
  • 可见,程序分别加入了第2行和第16行和中间的根据auth_type参数的判断的相关内容后, 就解决上述问题了。对于上面的这一个三层嵌套的相关逻辑,大家可以在 pycharm里头加上断点,逐步调试,便可发现其中的道理。

  • 总结

    • 要想学好迭代器就必须理解一下三条:

    • 1.函数即变量(即函数对象的概念)

    • 2.函数嵌套

    • 3.函数式编程

原文链接:http://www.cnblogs.com/Akkbe-chao/p/6980332.html

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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,585评论 18 139
  • 背景 虽然之前看过装饰器的相关内容,但是今天想起来,一直没有好好总结一下,所以特地记录下关于装饰器的一系列用法。要...
    楼上小宇阅读 688评论 0 3
  • 孤鸿远游人飘零, 轻语别离自思量。 飞絮迷离时节易, 陌上花开君神伤。
    云高不慕阅读 116评论 3 3
  • 9月12日,早5点,快读杨澜《人工智能,真的来了》。 人工智能(AI:Artificial Intelligenc...
    in喜悦阅读 589评论 0 0
  • 我一直以为,爱情是找到一个互相理解和包容的人,不必过多解释就都懂得,优点缺点都能喜欢,好像,另一个自己。可是,...
    郑正正zgy阅读 213评论 0 0