让Python的装饰器不再难理解

注意:

如果对闭包不了解的同学请移步到这里先,因为装饰器要通过闭包来实现

前言

刚开始学Python的时候,装饰器(decorator)一直是个让人难以理解的东西,所以想通过这篇文章能够带你一步一步来理解Python装饰器的原理

什么是装饰器?

经典的设计模式有23种,设计模式其实也就是巨人们常年写代码经验的思想总结,虽然说这是一种思想,但是由于语法的限制没有办法轻易实现(比如说用C语言来实现组合模式).在面向对象的设计模式中, decorator被称为装饰模式,OOP的装饰模式需要通过继承和组合来实现,而Python除了能支持OOP的decorator外,直接从语法层次支持decorator。

下面开始介绍装饰器的原理

业务逻辑:

假如你是简书的开发者,刚开发了一个发布文章的功能:

def send():
    # 核心代码
    print("发布成功")

然后你美滋滋得上传代码了,第二天产品经理拿着刀来找你:你妹啊,随便就能发布文章了?不用登录的?

~.~然后还得再写一个验证登录的逻辑,好了,写完了:

def check_login():
    print("做登录验证")

然后你就开始纠结了,这段代码怎么放呢?这样?

def send():
    check_login()
    # 核心代码
    print("发布成功")

这样确实完成了功能,但是不太优雅,耦合度太高,我们是要写出高质量代码的攻城狮!!!咳咳

所以,我们希望在不改变send()代码的前提下,还能做验证登录的操作,装饰器就出现了

def check_login(func):
    def inner():
        print("做登录验证")
        print("开始发布文章")
        func()
    return inner

def send():
    # 核心代码
    print("发布成功")

send = check_login(send) #0
send()

可以看到,注释0处的代码返回值已经是inner函数对象了.在执行send()的时候实际上就是在执行inner(),这样就能做到不改变原有函数代码的前提下,提升函数的功能!

Python中,有一个语法糖可以不用写注释0处的代码

@check_login #语法糖
def send():
    # 核心代码
    print("发布成功")

调用send()结果一样,所以说

@check_login #语法糖
def send():
    # 核心代码
    print("发布成功")

send = check_login(send)

两种写法是等价的!

在上面的代码可以看出来,装饰器个函数,它的参数是被装饰的函数,返回值也是一个函数.

业务逻辑的变动

有一天你发现,发布文章的代码好像得接收用户编写文章的内容和标题才能发布出现显示给用户看哦(不然就是一堆假数据),然后你赶紧改了一下发布文章的逻辑代码:

def send(title,content):
    # 核心代码
    print("文章标题: %s, 内容:%s" % (title,content))

运行就报错了,因为加上装饰器之后调用send("title","content")是相当于调用了inner(),但是装饰器里的inner并没有接收参数,所以,应该修改装饰器中的代码:

def check_login(func):
    def inner(*args, **kw):
        print("做登录验证")
        print("开始发布文章")
        func(*args,**kw) #0
    return inner

因为注释0代码处才是真正调用了发布文章的原函数,所以得把参数传回去,这样就解决问题了,可以传任意数量和任意类型的参数!!

新的问题

当你想要拿到发布文章的结果,发布成功或者失败:所以你得改发布文章的逻辑代码:

@check_login
def send(title,content):
    # 核心代码
    print("文章标题: %s, 内容:%s" % (title,content))
    return True

# 然后接收
result = send("Python","Python的装饰器")
print(result)

输出结果:
做登录验证
开始发布文章
文章标题: Python, 内容:Python的装饰器
None

emmmmm? result怎么回事None呢?如果你能看懂前面内容的话这个小bug对你来说就根本不在话下,修改装饰器的代码:

def check_login(func):
    def inner(*args, **kw):
        print("做登录验证")
        print("开始发布文章")
        func(*args,**kw)
    return inner

好了,一步一步到这.一个标准的装饰器终于完成了!!

装饰器的进阶

上面我们验证登录的装饰器代码中,验证完之后都会print("开始发布文章")来做调试.但是,我们不止发布文章的时候要做验证登录的操作,发图片,评论啊,私信啊,回复等等等...都是需要登录验证的装饰器(装饰器的优势体现出来了).然而,你在做其他操作的时候控制台来了一句开始发布文章 (黑人问号脸????),这时候你就想,要是调试的语句可以控制就好了

装饰器工厂

装饰器是可以接收自定义参数的,然后返回另一个装饰器.这样看来的话外面的装饰器其实就是个装饰器工厂,根据传来不同的参数生成不同的装饰器:

def get_decorator(console):
    def check_log(func):
        def inner(*args,**kw):
            print(console)
            return func(*args,**kw)
        return inner
    return check_log
    

@get_decorator("开始发布文章")
def send(title,content):
    # 核心代码
    print("文章标题: %s, 内容:%s" % (title,content))
    return True

result = send("Python","Python的装饰器")
print(result)

get_decorator就是个装饰器工厂,根据参数返回一个装饰器

上面的等价拆分的话,等价于

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

推荐阅读更多精彩内容

  • 本文为《爬着学Python》系列第四篇文章。从本篇开始,本专栏在顺序更新的基础上,会有不规则的更新。 在Pytho...
    SyPy阅读 2,489评论 4 11
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,510评论 25 707
  • 前言 Python的修饰器的英文名叫Decorator,当你看到这个英文名的时候,你可能会把其跟Design Pa...
    linheimx阅读 627评论 0 4
  • 刚刚在微信上看到一篇文章,里面有几句话是这么说的"你嘴上念念不忘的人,早就把你给忘记了;你一心一意喜欢的人,早就移...
    实心实意阅读 429评论 8 2
  • IconMarker 可以根据不同的平台生成对应的icon尺寸 下载地址:IconMarker
    lsdoy阅读 493评论 0 50