Python mini-web框架3:装饰器

一、看一个例子,来引出装饰器

需求:在不改变下面 test() 函数的情况下,执行函数的情况下先进行其他的步骤,看下面的实现

def set_func(func):

    print("----开始装饰----")

    def run_func():
        print("----验证权限1----")
        func()
    return run_func


def test1():
    print("----test----")

test1 = set_func(test1)
test1()

执行结果是:

----开始装饰----
----验证权限1----
----test----
引出装饰器
  • 分析:test1 = set_fun(test1) 改变了 test1() 的指向,指向了闭包内的run_func()函数,在test()执行的时候,执行的是run_func()函数内的代码
  • 优化: 不想写 test1 = set_fun(test1) 这句代码,我们可以在 def test1():上面写 @set_fun,我们在下面具体看使用。

二、装饰器的介绍以及参数

  • 2.1、简单的来说:一个闭包对一个函数进行装饰就组成了装饰器,如下的例子,@set_fun是一种 语法糖

    def set_func(func):
    
         print("----开始装饰----")
    
         def run_func():
    
             print("----验证权限1----")
             func()
         return run_func
    
    
    @set_func    # @set_fun 与 test1 = set_func(test1) 等价
    def test1():
    
        print("----test----")
    
    test1()
    

    提示: @set_fun 与 test1 = set_fun(test1) 等价

  • 2.2、装饰器对有 单个参数 函数的装饰(说白了就是传参)

    def set_func(func):
    
         print("----开始装饰----")
    
         def run_func(num):
    
              print("----验证权限1----")
              func(num)
         return run_func
    
    
    @set_func
    def test1(num):
    
         print("----test----%d"%num)
    
    test1(3)
    

    提示:test1指向的是闭包内的run_func函数,传的参数也是给run_func,函数内再调用test1函数,不要绕,好好理解下

  • 2.3、不定长参数的函数装饰(说白了就是参数不定),如下例子

    def set_func(func):
    
        print("----开始装饰----")
    
        def run_func(*args,**kwargs):
    
            print("----验证权限1----")
            func(*args,**kwargs)
        return run_func
    
    
    @set_func
    def test1(num,*args,**kwargs):
    
        print("----test----%d"%num,args,kwargs)
    
    test1(3,2,1,a="1")
    

    打印结果是:

    ----开始装饰----
    ----验证权限1----
    ----test----3 (2, 1) {'a': '1'}
    

    注意:

    • 闭包 里面的 func(*args,**kwargs) 中的 *args**kwargs是解包的意思,传参不能直接传元组与字典,要解包
    • 不可以传 func(args,kwargs),必挂,这样是传了两个参数,一个元组,一个字典,是不对的
  • 2.4、对应有返回值函数进行装饰、通用装饰器

    def set_func(func):
    
          print("----开始装饰----")
    
          def run_func(*args,**kwargs):
    
                print("----验证权限1----")
                return func(*args,**kwargs) # 拆包
          return run_func
    
    
    @set_func
    def test1(num,*args,**kwargs):
    
          print("----test----%d"%num,args,kwargs)
          return "OK"
    
    @set_func
    def test2():
    
          return "OK2"
    
    test1(3,2,1,a="1")
    print(test2())
    

    打印结果是:

    ----开始装饰----
    ----开始装饰----
    ----验证权限1----
    ----test----3 (2, 1) {'a': '1'}
    ----验证权限1----
    OK2
    
对应有返回值函数进行装饰、通用装饰器
  • 2.5、多个装饰器对同一个函数进行装饰

    def set_func1(func):
    
         print("----开始装饰1----")
    
         def run_func1(*args,**kwargs):
    
             print("----验证权限1----")
             func(*args,**kwargs) # 拆包
         return run_func1
    
    
    def set_func2(func):
    
         print("----开始装饰2----")
    
         def run_func2(*args,**kwargs):
    
             print("----验证权限2----")
             func(*args,**kwargs) # 拆包
         return run_func2
    
    
    @set_func1
    @set_func2
    def test1(num,*args,**kwargs):
    
          print("----test----%d"%num,args,kwargs)
    
    test1(3,2,1,a="1")
    

    打印结果:

    ----开始装饰2----
    ----开始装饰1----
    ----验证权限1----
    ----验证权限2----
    ----test----3 (2, 1) {'a': '1'}
    

    提示:当多个装饰器装饰同一个函数的时候,装饰从下往上装饰,也就是上面例子中先装饰 set_func2,再装饰set_func1
    装饰器在调用之前就已经装好了,组成一个大的闭包空间;执行的时候,先执行后装饰的装饰器里面的代码,后执行前一个被装饰的代码,依次类推,最后执行第一个装饰的闭包内的代码。

  • 2.6、对于同一个装饰器,对多个函数进行装饰
    分析:对于同一个装饰器,对多个函数进行装饰的时候,在没调用之前就会进行装饰,每装饰一个就生成一个闭包。闭包内外部函数的变量指向原函数

    def set_func(func):
    
          print("----开始装饰----")
    
          def run_func(*args,**kwargs):
    
                 print("----验证权限----")
                 func(*args,**kwargs) # 拆包
          return run_func
    
    
    @set_func
    def test1(num,*args,**kwargs):
    
          print("----test1----%d"%num,args,kwargs)
    
    
    @set_func
    def test2(num,*args,**kwargs):
    
          print("----test2----%d"%num,args,kwargs)
    
    test1(3,2,1,a="1")
    test2(4,5,6,a="2")
    

    打印结果:

    ----开始装饰----
    ----开始装饰----
    ----验证权限----
    ----test1----3 (2, 1) {'a': '1'}
    ----验证权限----
    ----test2----4 (5, 6) {'a': '2'}
    
  • 2.7、用类对函数进行装饰

    class Test(object):
        """docstring for ClassName"""
        def __init__(self, func):
      
            self.func = func
    
        def __call__(self):
    
            print("这里是装饰器添加的功能")
            return self.func()
    
    
    @Test  # 相当于 get_str = Test(get_str)
    def get_str():
    
         return "测试"
    
    
    print(get_str())
    

    打印结果:

    这里是装饰器添加的功能
    测试
    

三、带有参数的装饰器,是装饰器加参数,如下

def set_level(level):

    def set_func(func):
    
        def call_func(*args,**kwargs):

           if level ==1 :
                print("权限验证-- %d"%level)
           elif level ==2 :
                print("权限验证-- %d"%level)
           else:
                print("权限验证-- %d"%level)

           func(*args,**kwargs)
        
        return call_func

    return set_func


@set_level(9)
def test1():

    print("------test1------")


test1()
  • 分析:上面就是给装饰器加参数set_level(9):
    • 1、调用set_level并且将9当做实参传递
    • 2、用上一步调用的返回值当做装饰器对test1函数进行装饰

总结:不要觉得装饰器很难,其实它就是:函数的指向的改变,以闭包为例,闭包作为另一个函数的装饰器,装饰后,在闭包的内部函数再去调用函数,其实就是在函数执行之前先调用闭包内的代码。

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

推荐阅读更多精彩内容

  • 包(lib)、模块(module) 在Python中,存在包和模块两个常见概念。 模块:编写Python代码的py...
    清清子衿木子水心阅读 3,807评论 0 27
  • 在学习Python的过程中,我相信有很多人和我一样,对Python的装饰器一直觉得很困惑,我也是困惑了好久,并通过...
    愚灬墨阅读 460评论 1 1
  • 写在前面的话 代码中的# > 表示的是输出结果 输入 使用input()函数 用法 注意input函数输出的均是字...
    FlyingLittlePG阅读 2,764评论 0 8
  • 1、登陆mysql数据库 mysql -u root -p 查看user表 mysql> use mysql;Da...
    lycium阅读 3,537评论 0 1
  • 凛冽寒风萧萧瑟,百草枯,叶归土。鹧鸪双飞乐争吵,盘旋飞,无地落。 伊人红妆为谁翘,粉黛娇颜忙嬉闹。独吾一人哂笑。
    伊晓一阅读 222评论 2 1