python進(jìn)階--閉包和裝飾器

閉包和裝飾器

閉包

  1. 定義:在函數(shù)嵌套的前提下斩例,內(nèi)部函數(shù)使用了外部函數(shù)的變量娃胆,并且外部函數(shù)返回了內(nèi)部函數(shù)接箫,我們把這個(gè)使用外部函數(shù)變量的內(nèi)部函數(shù)稱為閉包博投。

  2. 閉包的條件:

    1. 函數(shù)嵌套
    2. 內(nèi)部函數(shù)使用外部函數(shù)的變量或者參數(shù)
    3. 外部函數(shù)返回內(nèi)部函數(shù)的引用(即內(nèi)部函數(shù)的內(nèi)存地址)
  3. 簡(jiǎn)單的閉包示例代碼:

    def func_out():
        num1 = 10
        def func_inner(num2):
            result = num1 + num2
            print(result)
        return func_inner
    
    inner = func_out()
    inner(50)
    
  4. 閉包的作用:

    • 閉包可以保存外部函數(shù)內(nèi)的變量,不會(huì)隨著外部函數(shù)調(diào)用完而銷毀
  5. 閉包的缺點(diǎn):由于閉包引用了外部函數(shù)的變量加袋,則外部函數(shù)的變量沒有及時(shí)釋放凛辣,消耗內(nèi)存。

  6. 閉包的應(yīng)用場(chǎng)景:

    • 閉包是在不改變?cè)泻瘮?shù)的調(diào)用方法和代碼的基礎(chǔ)上职烧,對(duì)原有的功能進(jìn)行增加
    • 閉包大大的增加了代碼的重用性蟀给,一個(gè)寫好的函數(shù)可以供多人調(diào)用,而且還不會(huì)更改代碼的結(jié)構(gòu)
  7. 內(nèi)部函數(shù)如何修改外部函數(shù)的變量

    方法:使用nonlocal

    示例代碼:

    def fun_out():
        num = 10
        def funn_inner():
            nonlocal num
            num = 100
            print(num)
        print(num)
        funn_inner()
        print(num)
        return funn_inner
    
    inner = fun_out()
    inner()
    

裝飾器

  1. 定義:就是給已有函數(shù)增加額外功能的函數(shù)阳堕,它本質(zhì)上就是一個(gè)閉包函數(shù)跋理。

  2. 裝飾器的特點(diǎn):

    1. 不修改已有函數(shù)的的源代碼
    2. 不修改已有函數(shù)的調(diào)用方式
    3. 給已有的函數(shù)增加額外的功能
  3. 裝飾器的示例代碼:

    # 寫個(gè)閉包
    def fun(func):
        def inner():
            print("登錄")
            func()
            print("評(píng)論成功")
        return inner
    
    # 被裝飾的函數(shù)
    @fun  # comment = fun(comment) 就相當(dāng)于@fun
    def comment():
        print("發(fā)表評(píng)論")
    # comment = fun(comment)
    comment()
    # 裝飾器和閉包的區(qū)別: 1.裝飾器是一種閉包
    #                   2.裝飾器的外部函數(shù)的參數(shù)是被裝飾的函數(shù)
    #                   3.其實(shí)就是把內(nèi)部函數(shù)賦值給與被裝飾函數(shù)同名的函數(shù)名,然后正常調(diào)用被裝飾的函數(shù)
    

    示例說明:

    • 閉包函數(shù)有且只有一個(gè)參數(shù)恬总,必須是函數(shù)類型前普,這樣定義的函數(shù)才是裝飾器。
    • 寫代碼要遵循開放封閉原則壹堰,它規(guī)定已經(jīng)實(shí)現(xiàn)的功能代碼不允許被修改拭卿,但可以被擴(kuò)展。
    • @fun語(yǔ)法糖是另一種裝飾器的寫法贱纠,和傳統(tǒng)的寫法是一樣的峻厚。以后我們經(jīng)常用到的也會(huì)是語(yǔ)法糖的格式
  4. 裝飾器的使用場(chǎng)景:

    1. 函數(shù)執(zhí)行時(shí)間的統(tǒng)計(jì)

      import time
      def make_time(fun):
          def inner():
              start = time.time()
              fun()
              end = time.time()
              print(end - start)
          return inner
      @make_time
      def test():
          for i in range(10000000):
              print(i)
      test()
      
    2. 輸出日志文件

  5. 裝飾帶有參數(shù)的函數(shù)

    • 示例代碼:

      def log(fun):
          def add_lig(num1, num2):  # 因?yàn)檫@里相當(dāng)于用inner來替代test,所以test有幾個(gè)參數(shù)谆焊,inner就應(yīng)該有幾個(gè)參數(shù)
              print("這是一個(gè)加法運(yùn)算")
              fun(num1, num2)
          return add_lig
      
      
      @log
      def test(num1, num2):
          result = num1 + num2
          print(result)
      
      
      test(10, 20)
      # 結(jié)論:內(nèi)部函數(shù)的參數(shù)必須與被裝飾的函數(shù)的參數(shù)一致,也要與內(nèi)部函數(shù)調(diào)用的被裝飾的函數(shù)的參數(shù)一致
      
  6. 裝飾帶有返回值的函數(shù)

    • 示例代碼:

      def log(fun):
          def add_lig(num1, num2):  # 因?yàn)檫@里相當(dāng)于用inner來替代test惠桃,所以test有幾個(gè)參數(shù),inner就應(yīng)該有幾個(gè)參數(shù)
              print("這是一個(gè)加法運(yùn)算")
              return fun(num1, num2)
          return add_lig
      @log
      def test(num1, num2):
          result = num1 + num2
          return result
      
      
      rs = test(10, 20)
      print(rs)
      # 結(jié)論:如果被裝飾的函數(shù)有返回值辖试,那么內(nèi)部函數(shù)中就必須有返回值.
      
  7. 通用裝飾器

    • 通用裝飾器就是被裝飾函數(shù)是可以傳遞任意參數(shù)的函數(shù)辜王,

    • 示例代碼:

      def log(fun):
          def inner(num, *args, **kwargs):
              print("正在計(jì)算中")
              return fun(num,*args, **kwargs)
          return inner
      
      
      def test(num, *args, **kwargs):
          result = num
          for num in args:
              result += num
          for num in kwargs.values():
              result += num
          return result
      
      
      result = test(10, 20, 30, a=20, b=30)
      print(result)
      # 總結(jié):內(nèi)部函數(shù),被裝飾函數(shù),內(nèi)部函數(shù)調(diào)用的函數(shù) 他們的參數(shù)必須一模一樣
      
  1. 多裝飾器的使用:

    • 多裝飾器就是多個(gè)裝飾器同時(shí)裝飾一個(gè)函數(shù)

    • 代碼示例:

      # 使用div標(biāo)簽把內(nèi)容包裹起來
      def make_div(fun):
          print("--------")
          def inner():
              result = "<div>" + fun() + "</div>"
              return result
          return inner
      
      
      # 使用p標(biāo)簽把內(nèi)容包裹起來
      def make_p(fun):
          print("+++++")
          def inner():
              result = "<p>" + fun() + "</p>"
              return result
          return inner
      @make_div
      @make_p
      def content():
          return "這是多裝飾器的使用代碼"
      rs = content()
      print(rs)
      # 總結(jié): 多重裝飾器先使用靠近被裝飾函數(shù)的裝飾器,類似于穿衣服罐孝,從下到上呐馆,從里到外
      

      代碼說明:當(dāng)你調(diào)用content函數(shù)的時(shí)候,函數(shù)會(huì)先執(zhí)行離它最近的那個(gè)裝飾器make_P里面的代碼莲兢,也就是U先打印"--------"汹来,然后內(nèi)容變成了<p>這是多裝飾器的使用代碼</p>,然后會(huì)執(zhí)行make_div里面的代碼,打印出+++++改艇,最后打印<div><p>這是多裝飾器的使用代碼</p></div>收班。

  2. 由前面的學(xué)習(xí)內(nèi)容可以知道裝飾器的外部函數(shù)能接受一個(gè)類型為函數(shù)的參數(shù)。即引用被裝飾的函數(shù)遣耍。

    那么裝飾器的外部函數(shù)能不能直接傳遞其他的參數(shù)呢闺阱?

    答案是:不能。

    代碼如下:

    def decorator(fn, flag):
        def inner(num1, num2):
            if flag == "+":
                print("--正在努力加法計(jì)算--")
            elif flag == "-":
                print("--正在努力減法計(jì)算--")
            result = fn(num1, num2)
            return result
        return inner
    
    
    @decorator('+')
    def add(a, b):
        result = a + b
        return result
    
    result = add(1, 3)
    print(result)
    
    

    這里運(yùn)行之后舵变,會(huì)報(bào)錯(cuò)酣溃。那要怎樣才能實(shí)現(xiàn)裝飾器里面?zhèn)鬟f參數(shù)呢瘦穆?

    在裝飾器的最前面再加上一個(gè)函數(shù),這個(gè)函數(shù)里面可以傳參赊豌。代碼如下:

    # 帶有參數(shù)的裝飾器扛或,要在裝飾器前面加上一個(gè)函數(shù),結(jié)束的時(shí)候返回一個(gè)函數(shù)
    def log(n):
        def test(fun):
            def inner(num1, num2):
                if n == "+":
                    print("正在計(jì)算加法碘饼。")
                    return fun(num1, num2)
                if n == "-":
                    print("正在計(jì)算減法")
                    return fun(num1, num2)
            return inner
        return test
    
    
    
    def sum_num(num1,num2):
        result = num1 + num2
        return result
    test = log("+")
    sum_num = test(sum_num)
    @log("-")
    def sub_num(num1,num2):
        result = num1 - num2
        return result
    
    rs = sum_num(20, 10)
    print(rs)
    rs1 = sub_num(20, 10)
    print(rs1)
    
    

    這樣就能在裝飾器里面?zhèn)鬟f其他的參數(shù)了熙兔。

    注意點(diǎn):

    1. 調(diào)用裝飾器后@后要調(diào)用最外部的那個(gè)函數(shù),不再是之前的那個(gè)函數(shù)了艾恼。
    2. 一定要返回之前的那個(gè)裝飾器函數(shù)的引用住涉。

今天的分享就到這里了,如果哪里出錯(cuò)了钠绍,還請(qǐng)?jiān)谠u(píng)論區(qū)指正出來舆声,我會(huì)積極改正。并且歡迎探討python有關(guān)的知識(shí)柳爽。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末媳握,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子磷脯,更是在濱河造成了極大的恐慌蛾找,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赵誓,死亡現(xiàn)場(chǎng)離奇詭異打毛,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)架曹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門隘冲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來闹瞧,“玉大人绑雄,你說我怎么就攤上這事“掠剩” “怎么了万牺?”我有些...
    開封第一講書人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)洽腺。 經(jīng)常有香客問我脚粟,道長(zhǎng),這世上最難降的妖魔是什么蘸朋? 我笑而不...
    開封第一講書人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任核无,我火速辦了婚禮,結(jié)果婚禮上藕坯,老公的妹妹穿的比我還像新娘团南。我一直安慰自己噪沙,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開白布吐根。 她就那樣靜靜地躺著正歼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪拷橘。 梳的紋絲不亂的頭發(fā)上局义,一...
    開封第一講書人閱讀 52,268評(píng)論 1 309
  • 那天,我揣著相機(jī)與錄音冗疮,去河邊找鬼萄唇。 笑死,一個(gè)胖子當(dāng)著我的面吹牛术幔,可吹牛的內(nèi)容都是我干的穷绵。 我是一名探鬼主播,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼特愿,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼仲墨!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起揍障,我...
    開封第一講書人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤目养,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后毒嫡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體癌蚁,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年兜畸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了努释。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡咬摇,死狀恐怖伐蒂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情肛鹏,我是刑警寧澤逸邦,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站在扰,受9級(jí)特大地震影響缕减,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜芒珠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一桥狡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦裹芝、人聲如沸呈宇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)甥啄。三九已至,卻和暖如春炬搭,著一層夾襖步出監(jiān)牢的瞬間蜈漓,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工宫盔, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留融虽,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓灼芭,卻偏偏與公主長(zhǎng)得像有额,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子彼绷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • 一巍佑、函數(shù)的特殊用法 1.變量可以指向函數(shù) 代碼演示:#abs------>absolute#abs()是一個(gè)系統(tǒng)的...
    鄭元吉閱讀 285評(píng)論 0 1
  • 一、函數(shù)的特殊用法 1.變量可以指向函數(shù) 代碼演示:#abs------>absolute#abs()是一個(gè)系統(tǒng)的...
    hollow_02f9閱讀 674評(píng)論 0 0
  • 一、迭代器 1.可迭代對(duì)象 可迭代對(duì)象【實(shí)體】:可以直接作用于for循環(huán)的實(shí)體【Iterable】可以直接作用于f...
    hollow_02f9閱讀 522評(píng)論 0 0
  • 【Day 7】 夜起如廁猜旬,忽見窗前立一白須老者脆栋,驚。 老者忙道洒擦,“靚女莫慌椿争,吾乃記憶老君也。見你日日憂思記憶力退化...
    我是凱茜閱讀 283評(píng)論 2 4
  • 你是否有這樣的困擾:你和其他人(同學(xué)熟嫩、同事秦踪、朋友、家人)明明執(zhí)行了同樣的行為邦危,得到的結(jié)果卻截然不同洋侨。同坐在一個(gè)教室...
    孜姿不倦閱讀 6,004評(píng)論 2 5