开心六月综合激情婷婷|欧美精品成人动漫二区|国产中文字幕综合色|亚洲人在线成视频

    1. 
      
        <b id="zqfy3"><legend id="zqfy3"><fieldset id="zqfy3"></fieldset></legend></b>
          <ul id="zqfy3"></ul>
          <blockquote id="zqfy3"><strong id="zqfy3"><dfn id="zqfy3"></dfn></strong></blockquote>
          <blockquote id="zqfy3"><legend id="zqfy3"></legend></blockquote>
          打開APP
          userphoto
          未登錄

          開通VIP,暢享免費電子書等14項超值服

          開通VIP
          剛轉行1年測開新手:學習編程幾點經(jīng)驗分享

          干貨技術,第一時間送達!

          一、開頭說兩句

          大家好,我叫黎潘,很高興有機會給大家分享一些我的學習感受,作為一名零基礎轉行剛一年的測試新手來說,深知自己在技術經(jīng)驗方面落后太多,難免會有急于求成的心態(tài),這也就導致自己在學習新知識時似懂非懂,剛開始學完那會還胸有成竹,一段時間之后卻又忘的一干二凈,導致我要不?;厝土?,還始終不得要領,難以在實踐中靈活運用。

          相信有不少同學跟我一樣徘徊躊躇,現(xiàn)在老師給予了我一個給大家分享經(jīng)驗的機會,我也剛好結合前段時間復習關于Python裝飾器的理解來說下,若有不對的地方,還望各位同學,同行,老師及時指出。

          二、裝飾器必知基礎

          其實很多知識點沒有牢牢掌握,是因為最最基礎的知識沒有理解透徹導致。這也是我在學習裝飾器時對于自己的評價,所以先讓我們來聊聊學習裝飾器所需要的基礎知識。

          1、形參與實參

          函數(shù)的參數(shù)分為形式參數(shù)實際參數(shù),簡稱形參和實參。

          • 形參即在定義函數(shù)時,括號內(nèi)聲明的參數(shù)。形參本質就是一個變量,用來接收外部傳來的值
          • 實參即在調(diào)用函數(shù)時,括號內(nèi)傳入的值,值可以是常量,變量,表達式或三者的組合

          具體使用時又分為位置參數(shù),關鍵字參數(shù)和默認參數(shù)

          def info(name,age,sex='male')
           print(f'name:{name} age:{age} sex:{sex}')
          info(name='jack',18)

          上述示例中,調(diào)用函數(shù)時以key=value形式的就是關鍵字參數(shù),定義函數(shù)時name,age為位置參數(shù),sex為默認參數(shù)。

          注意

          • 調(diào)用函數(shù)時,實參可以是按位置或關鍵字的混合使用,但必須保證關鍵字參數(shù)在位置參數(shù)后面,且不可以對一個形參重復賦值
          • 默認參數(shù)的值通常應設為不可變類型

          2、可變長度參數(shù)*args和**kwargs

          參數(shù)的長度可變指的是調(diào)用函數(shù)時,實參的個數(shù)可以不固定,而在調(diào)用階段,實參無非是按照位置或者按關鍵字兩種形式,因此就出現(xiàn)了兩種解決方案來處理。

          2.1 可變長度的位置參數(shù)

          如果在最后一個形參名前加*號,那么在調(diào)用函數(shù)時,溢出的位置實參都會被接受,以元組的形式保存下來賦值給該形參。

          def func(x,y,z=1,*args):
              print(x,y,z,args)
          func(1,2,3,4,5,6,7)
          >>1 2 3 (4,5,6,7)
          #這里起作用的就是*號,相當于溢出的位置參數(shù)賦值給了它后面的變量,即args=(4,5,6,7)

          2.2 可變長度的關鍵字參數(shù)

          如果在最后一個形參名前加**號,那么在調(diào)用函數(shù)時,溢出的關鍵字參數(shù),會以字典的形式保存下來賦值給形參。

          def func(x,**kwargs):
              print(x)
              print(kwargs)
          func(x=1,y=2,z=3)
          >>1
          >>{'y':2,'z':3}

          #同上此時相當于把溢出的關鍵字實參一,y,z都被**接收以字典的形式賦值給kwargs,即kwargs={'y':2,'z':3}

          2.3 組合使用

          可變參數(shù)*args與關鍵字參數(shù)kwargs通常是組合在一起使用的,如果一個函數(shù)的形參為上述兩種類型,那么代表該函數(shù)可以接收任何形式,任意長度的參數(shù)。

          def wrapper(*args,**kwargs):
              pass

          在該函數(shù)內(nèi)部還可以把接受到的實參傳給另一個函數(shù),這在后面推導裝飾器時大有用處。

          def func(x,y,z):
              print(x,y,z)
          def wrapper(*args,**kwargs):
              func(*args,**kwargs)
          wrapper(1,y=2,z=3)
          >>1 2 3

          分析

          此處在給wrapper傳參時,其遵循的事函數(shù)func的參數(shù)規(guī)則,第一步,位置參數(shù)1被接受,以元組形式保存下來賦值給args,即args=(1,),關鍵字參數(shù)y=2,z=3被**以字典形式接收賦值給kwargs,即kwargs={'y':2,'z':3};第二步,執(zhí)行func(args,kwargs),即func((1,),{'y':2,'z':3}),等同于func(1,y=2,z=3)。

          3、函數(shù)對象和閉包

          函數(shù)對象指的是函數(shù)可以被當做"數(shù)據(jù)"來處理,具體可以分為四個方面的使用

          3.1 函數(shù)可以被引用

          def add(x,y):
              return x+y
          func = add
          func(1,2)
          >>3

          3.2 函數(shù)可以作為容器類型的元素

          dic = {'add':add}
          >>dic
          >>{'add': <function add at 0x100661e18>}
          >>dic['add'](1,2)
          >>3

          3.3  函數(shù)可以作為參數(shù)傳入另一個函數(shù)

          def foo(x,y,func):
              return fun(x,y)
          >>foo(1,2,add)
          >>3

          3.4 函數(shù)的返回值可以是一個函數(shù)

          def bdd():
              return add
          func=bdd()
          func(1,2)
          >>3

          3.5 閉包函數(shù)有兩個關鍵點

          • "閉":值得時函數(shù)定義在另一個函數(shù)內(nèi)即內(nèi)嵌函數(shù)。

          • "包":指的是該函數(shù)包含對外層函數(shù)作用于變量的引用。

            def f1():
                x = 1
                def f2():
                    print(x)
             f2()
            #此時f2就是內(nèi)嵌函數(shù),為'閉’,f2有對外層變量x的引用,為'包’
            #但是我們不想在內(nèi)部調(diào)用f2函數(shù)該怎么辦呢
            #這個時候函數(shù)對象的引用,可以作為返回對象就可以解決,即:
            def f1():
                x = 1
                def f2():
                    print(x)
             return f2 #注意不能加括號,否者就是返回f2的執(zhí)行結果,我們需要的是他的內(nèi)存地址以供在外部可以隨時調(diào)用
            f = f1() #此刻變量f接受到的就是f2的內(nèi)存地址

          總結:

          閉包函數(shù)提供了一種新的為函數(shù)體傳參的方式,為了給f2傳值,在他的同級作用域給了他一個值,f2在整體縮進,外層再給他嵌套一個函數(shù)f1包起來。此時f1從原來的全局變成了局部,為了使我們在全局依然可以調(diào)用它,通過return函數(shù)對象再返回到全局。

          三、什么是裝飾器

          上邊講了這么多,可能大家有點疑惑怎么還不介紹裝飾器。不用急,這也是我們在學習中常犯的錯誤,急于求成反而不利于對知識的吸收好消化理解。其實在潛移默化中,我們已經(jīng)把大部分構成裝飾器的基本知識提到了,只是還未進行歸納整理。下面我們又將重新一步一步推導它的由來。

          定義:定義一個函數(shù)(類),該函數(shù)專門用來為其他函數(shù)(對象)添加額外的功能。

          裝飾器本質上是一個python函數(shù)或類,它可以讓其他函數(shù)或類在不需要做任何代碼修改的前提下增加額外功能,裝飾器的返回值也是一個函數(shù)/類對象。它經(jīng)常用于有切面需求的場景,比如:插入日志,性能測試,事務處理,緩存,權限校驗等場景。

          四、為什么用裝飾器

          我們在為一個對象添加新功能時,往往秉持著開放封閉原則。

          • 開放:指的是對拓展功能是開放的
          • 封閉:指的是修改源代碼是封閉的

          即在不修改被裝飾對象源代碼和調(diào)用方式的情況下為被裝飾對象新增功能。有了裝飾器,我們就可以抽離出大量與函數(shù)功能本身無關的雷同代碼到裝飾器中并繼續(xù)重用。

          五、裝飾器的推導

          提出需求:為index函數(shù)新增計算代碼運行時間的功能,必須符合開放封閉原則。

          import time
          def index(x,y):
              time.sleep(2)
              print(f'來自index的{x}{y}')

          1、方案一

          def index(x, y):
              start = time.time()
              time.sleep(2)
              print(f"來自index的{x}{y}")
              stop = time.time()
              print(stop-start)

          結果:雖然實現(xiàn)了功能,但破環(huán)了開放封閉原則,修改了源代碼,不符合要求,失敗

          2、方案二

          start = time.time()
          index(12)
          stop = time.time()
          print(stop-start)

          結果:上述代碼雖然沒有修改源代碼,也實現(xiàn)了功能,但是每次使用都要加上這三行代碼,太過冗余,失敗。

          3、方案三

          '''在方案二的基礎上進行優(yōu)化,為了解決代碼冗余,我們把它寫成一個函數(shù)'''
          def wrapper():
              start = time.time()
              index(12)
              stop = time.time()
              print(stop-start)
          wrapper()

          結果:此時我們不用每次加上三行代碼,只需調(diào)用wrapper函數(shù)即可,但是復用性依然不夠,可以在進行優(yōu)化

          4、方案四

          優(yōu)化:解決index的傳參被寫死了的問題

          #此時函數(shù)參數(shù)的知識就用上了
          def wrapper(a, b):
              start = time.time()
              index(a, b)
              stop = time.time()
              print(stop-start)
          wrapper(1,2)
          #但是可能在后續(xù)的需求中index的傳參個數(shù)會發(fā)生變化
          #此時可變長度參數(shù)就能幫上大忙了
          def wrapper(*args, **kwargs):
              start = time.time()
              index(*args, **kwargs)
              stop = time.time()
              print(stop-start)
          '''這個時候就不用擔心給他傳參數(shù)的問題了,wrapper收到什么參數(shù)都會原封不動交給index函數(shù)'''

          結果:進行了一系列的優(yōu)化,我們發(fā)現(xiàn)雖然傳參的問題解決了,但是這個時候index函數(shù)也寫死了,以后的需求中不可能只有它需要這個功能,復用性不夠,因此可以繼續(xù)優(yōu)化。

          5、方案五

          優(yōu)化:index寫死了的問題

          '''我們知道一旦某個變量寫死了,那么我們就用一個變量去代替他,但是在wrapper函數(shù)中,index寫成變量后,無法通過形參傳給他,這個時候閉包函數(shù)就大顯神威了,它就提供了一種給函數(shù)傳參的方式'''
          def outter(func):
              #func = index  #寫活
              def wrapper(*args, **kwargs):
                  start = time.time()
                  func(*args, **kwargs)
                  stop = time.time()
                  print(stop-start)
              return wrapper
          f = outter(index)
          '''返回的是wrapper的內(nèi)存地址賦值給f,加個括號就是在調(diào)用wrapper,它的作用就是計算以函數(shù)對象傳入其中的index的執(zhí)行時間統(tǒng)計'''
          # 為了不改變調(diào)用方式,在進行優(yōu)化
          # 可以把f = outter(index),為什么不可以賦值給index呢
          # 最后:index = outter(index) 即wrapper的內(nèi)存地址
          index() # 此時對于函數(shù)的調(diào)用者來說,他沒有變化,早就換了

          6、方案五

          優(yōu)化:上面看是已經(jīng)優(yōu)化得差不多了,其實還是有漏洞,原函數(shù)index是沒有返回值的,此時調(diào)用換掉之后的index之后,返回的時wrapper的內(nèi)存地址,它并沒有返回值,index()返回的是None,沒有做到天衣無縫。這個時候就要用到我們上面講到的函數(shù)對象的引用可以作為返回值,問題就迎刃而解了。

          def outter(func):
              #func = index  #寫活
              def wrapper(*args, **kwargs):
                  start = time.time()
                  res = func(*args, **kwargs)
                  stop = time.time()
                  print(stop-start)
                  return res
              return wrapper
          ''' 我們把func函數(shù)的返回值通過return,在返回出來,當我們運行index()時,實際上就是在調(diào)用wrapper,此刻它是有返回值的,也就是func的返回值,這個時候才做到了天衣無縫'''

          6、最終方案(推薦)

          def outter(func):
              def wrapper(*args, **kwargs):
                  res = func(*args, **kwargs)
                  return res
              return wrapper

          這就是一個最簡單的無參裝飾器的模版,我們想要給某個對象也就是func函數(shù)添加新功能時直接在wrapper函數(shù)內(nèi)部書寫代碼即可。

          關于有參裝飾器,此處由于篇幅限制就不在說明。有參也就說明我們的函數(shù)內(nèi)部需要一個參數(shù),無非就是兩種方式,一種通過形參直接傳入,另一種就是通過閉包函數(shù)直接包給它,此處肯定是利用閉包函數(shù)更合理。

          六、感言

          誤打誤撞,因為老師的一個課后作業(yè)任務,完成了本人的第一篇知識總結。剛開始是有點驚慌的,但是隨之而來的是驚喜,雖然擔心寫的不夠好,但也算是想給自己一個交代,一個好的開始。知識的持續(xù)分享總結能夠促進我們持續(xù)的學習進步。

          經(jīng)過這次小小的分享,回到開頭,我想說的就是學習一些高階知識,當我們感到模模糊糊的時候,不妨回歸本質,從最基礎的原理對他進行分解,一步一步推導,往往能給到我們一種醍醐灌頂,意想不到的收獲。最后希望同大家一道能通過這次的學習,提升自己,完成自己的初心。

          本站僅提供存儲服務,所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權內(nèi)容,請點擊舉報。
          打開APP,閱讀全文并永久保存 查看更多類似文章
          猜你喜歡
          類似文章
          理解 Python 裝飾器看這一篇就夠了 – 碼農(nóng)網(wǎng)
          Python裝飾器進階
          Python 裝飾器
          一位Python初學者的獨白:Python小白眼中的裝飾器
          Python Decorator 基礎
          python中強大的裝飾器
          更多類似文章 >>
          生活服務
          分享 收藏 導長圖 關注 下載文章
          綁定賬號成功
          后續(xù)可登錄賬號暢享VIP特權!
          如果VIP功能使用有故障,
          可點擊這里聯(lián)系客服!

          聯(lián)系客服