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

    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>
          打開(kāi)APP
          userphoto
          未登錄

          開(kāi)通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

          開(kāi)通VIP
          【Python之路】特別篇
          前情提要

          1. 作用域

            在python中,函數(shù)會(huì)創(chuàng)建一個(gè)新的作用域。python開(kāi)發(fā)者可能會(huì)說(shuō)函數(shù)有自己的命名空間,差不多一個(gè)意思。這意味著在函數(shù)內(nèi)部碰到一個(gè)變量的時(shí)候函數(shù)會(huì)優(yōu)先在自己的命名空間里面去尋找。

          python中的作用域分4種情況:

          • L:local,局部作用域,即函數(shù)中定義的變量;

          • E:enclosing,嵌套的父級(jí)函數(shù)的局部作用域,即包含此函數(shù)的上級(jí)函數(shù)的局部作用域,但不是全局的;

          • G:globa,全局變量,就是模塊級(jí)別定義的變量;

          • B:built-in,系統(tǒng)固定模塊里面的變量,比如int, bytearray等。

          搜索變量的優(yōu)先級(jí)順序依次是:作用域局部 > 外層作用域 > 當(dāng)前模塊中的全局 > python內(nèi)置作用域,也就是LEGB。

          當(dāng)然,local和enclosing是相對(duì)的,enclosing變量相對(duì)上層來(lái)說(shuō)也是local。

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          x = int(2.9# int built-in
            
          g_count = 0  # global
          def outer():
              o_count = 1  # enclosing
              def inner():
                  i_count = 2  # local
                  print(o_count)
              # print(i_count) 找不到
              inner()
          outer()
            
          # print(o_count) #找不到

          在Python中,只有模塊(module),類(class)以及函數(shù)(def、lambda)才會(huì)引入新的作用域,其它的代碼塊(如if、try、for等)是不會(huì)引入新的作用域的。

          讓我們寫一個(gè)簡(jiǎn)單的函數(shù)看一下 global 和 local 有什么不同:

          1
          2
          3
          4
          5
          6
          7
          >>> a_string = "This is a global variable"
          >>> def foo():
          ...     print locals()
          >>> print globals()
          {..., 'a_string': 'This is a global variable'}
          >>> foo() # 2
          {}

          內(nèi)置的函數(shù)globals返回一個(gè)包含所有python解釋器知道的變量名稱的字典。

          在#2我調(diào)用了函數(shù) foo 把函數(shù)內(nèi)部本地作用域里面的內(nèi)容打印出來(lái)。

          我們能夠看到,函數(shù)foo有自己獨(dú)立的命名空間,雖然暫時(shí)命名空間里面什么都還沒(méi)有。

          global關(guān)鍵字 

          當(dāng)內(nèi)部作用域想修改外部作用域的變量時(shí),就要用到global和nonlocal關(guān)鍵字了,當(dāng)修改的變量是在全局作用域(global作用域)上的,就要使用global先聲明一下,代碼如下:

          1
          2
          3
          4
          5
          6
          7
          8
          9
          count = 10
          def outer():
              global count
              print(count)
              count = 100
              print(count)
          outer()
          #10
          #100

          nonlocal關(guān)鍵字 

          global關(guān)鍵字聲明的變量必須在全局作用域上,不能嵌套作用域上,當(dāng)要修改嵌套作用域(enclosing作用域,外層非全局作用域)中的變量怎么辦呢,這時(shí)就需要nonlocal關(guān)鍵字了

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          def outer():
              count = 10
              def inner():
                  nonlocal count
                  count = 20
                  print(count)
              inner()
              print(count)
          outer()
          #20
          #20 

          2. 變量解析規(guī)則

          當(dāng)然這并不是說(shuō)我們?cè)诤瘮?shù)里面就不能訪問(wèn)外面的全局變量。

          在python的作用域規(guī)則里面,創(chuàng)建變量一定會(huì)一定會(huì)在當(dāng)前作用域里創(chuàng)建一個(gè)變量,

          但是訪問(wèn)或者修改變量時(shí)會(huì)先在當(dāng)前作用域查找變量,沒(méi)有找到匹配變量的話會(huì)依次向上在閉合的作用域里面進(jìn)行查看找。

          所以如果我們修改函數(shù)foo的實(shí)現(xiàn)讓它打印全局的作用域里的變量也是可以的:

          1
          2
          3
          4
          5
          >>> a_string = "This is a global variable"
          >>> def foo():
          ...     print a_string # 1
          >>> foo()
          This is a global variable

          在#1處,python解釋器會(huì)嘗試查找變量a_string,當(dāng)然在函數(shù)的本地作用域里面是找不到的,所以接著會(huì)去上層的作用域里面去查找。

          但是另一方面,假如我們?cè)诤瘮?shù)內(nèi)部給全局變量賦值,結(jié)果卻和我們想的不一樣:

          1
          2
          3
          4
          5
          6
          7
          8
          >>> a_string = "This is a global variable"
          >>> def foo():
          ...     a_string = "test" # 1
          ...     print locals()
          >>> foo()
          {'a_string': 'test'}
          >>> a_string # 2
          'This is a global variable'

          我們能夠看到,全局變量能夠被訪問(wèn)到(如果是可變數(shù)據(jù)類型(像list,dict這些)甚至能夠被更改)但是賦值不行。

          在函數(shù)內(nèi)部的#1處,我們實(shí)際上新創(chuàng)建了一個(gè)局部變量,隱藏全局作用域中的同名變量。

          我們可以通過(guò)打印出局部命名空間中的內(nèi)容得出這個(gè)結(jié)論。

          我們也能看到在#2處打印出來(lái)的變量a_string的值并沒(méi)有改變。

          3. 變量生存周期

          值得注意的一個(gè)點(diǎn)是,變量不僅是生存在一個(gè)個(gè)的命名空間內(nèi),他們都有自己的生存周期,請(qǐng)看下面這個(gè)例子:

          1
          2
          3
          4
          5
          6
          7
          >>> def foo():
          ...     x = 1
          >>> foo()
          >>> print x # 1
          Traceback (most recent call last):
            ...
          NameError: name 'x' is not defined

          #1處發(fā)生的錯(cuò)誤不僅僅是因?yàn)?code>作用域規(guī)則導(dǎo)致的(盡管這是拋出了NameError的錯(cuò)誤的原因)

          它還和python以及其它很多編程語(yǔ)言中函數(shù)調(diào)用實(shí)現(xiàn)的機(jī)制有關(guān)。

          在這個(gè)地方這個(gè)執(zhí)行時(shí)間點(diǎn)并沒(méi)有什么有效的語(yǔ)法讓我們能夠獲取變量x的值,因?yàn)樗@個(gè)時(shí)候壓根不存在!

          函數(shù)foo的命名空間隨著函數(shù)調(diào)用開(kāi)始而開(kāi)始,結(jié)束而銷毀。

          4. 嵌套函數(shù)

          Python允許創(chuàng)建嵌套函數(shù)。這意味著我們可以在函數(shù)里面定義函數(shù)而且現(xiàn)有的作用域和變量生存周期依舊適用。

          1
          2
          3
          4
          5
          6
          7
          8
          >>> def outer():
          ...     x = 1
          ...     def inner():
          ...         print x # 1
          ...     inner() # 2
          ...
          >>> outer()
          1

          python解釋器需找一個(gè)叫x的本地變量,查找失敗之后會(huì)繼續(xù)在上層的作用域里面尋找。

          這個(gè)上層的作用域定義在另外一個(gè)函數(shù)里面。

          對(duì)函數(shù)outer來(lái)說(shuō),變量x是一個(gè)本地變量,但是如先前提到的一樣,

          函數(shù)inner可以訪問(wèn)封閉的作用域(至少可以讀和修改)。

          在#2處,我們調(diào)用函數(shù)inner,非常重要的一點(diǎn)是:

          inner也僅僅是一個(gè)遵循python變量解析規(guī)則的變量名,python解釋器會(huì)優(yōu)先在outer的作用域里面對(duì)變量名inner查找匹配的變量.

          5. 閉包

          我們先不急著定義什么是閉包,先來(lái)看看一段代碼:

          1
          2
          3
          4
          5
          6
          7
          8
          >>> def outer():
          ...     x = 1
          ...     def inner():
          ...         print x # 1
          ...     return inner
          >>> foo = outer()
          >>> foo.func_closure
          (<cell at 0x...: int object at 0x...>,)

          inner作為一個(gè)函數(shù)被outer返回,保存在一個(gè)變量foo,并且我們能夠?qū)λM(jìn)行調(diào)用foo()。

          不過(guò)它會(huì)正常的運(yùn)行嗎?我們先來(lái)看看作用域規(guī)則。

          所有的東西都在python的作用域規(guī)則下進(jìn)行工作:“x是函數(shù)outer里的一個(gè)局部變量。

          當(dāng)函數(shù)inner在#1處打印x的時(shí)候,python解釋器會(huì)在inner內(nèi)部查找相應(yīng)的變量,當(dāng)然會(huì)找不到,

          所以接著會(huì)到封閉作用域里面查找,并且會(huì)找到匹配。

          但是從變量的生存周期來(lái)看,該怎么理解呢?

          我們的變量x是函數(shù)outer的一個(gè)本地變量,這意味著只有當(dāng)函數(shù)outer正在運(yùn)行的時(shí)候才會(huì)存在。

          根據(jù)我們已知的python運(yùn)行模式,我們沒(méi)法在函數(shù)outer返回之后繼續(xù)調(diào)用函數(shù)inner,在函數(shù)inner被調(diào)用的時(shí)候,變量x早已不復(fù)存在,可能會(huì)發(fā)生一個(gè)運(yùn)行時(shí)錯(cuò)誤。

          萬(wàn)萬(wàn)沒(méi)想到,返回的函數(shù)inner居然能夠正常工作。

          Python支持一個(gè)叫做函數(shù)閉包的特性,用人話來(lái)講就是:

          嵌套定義在非全局作用域里面的函數(shù)能夠記住它在被定義的時(shí)候它所處的封閉命名空間。

          這能夠通過(guò)查看函數(shù)的func_closure屬性得出結(jié)論,這個(gè)屬性里面包含封閉作用域里面的值

          (只會(huì)包含被捕捉到的值,比如x,如果在outer里面還定義了其他的值,封閉作用域里面是不會(huì)有的)

          記住,每次函數(shù)outer被調(diào)用的時(shí)候,函數(shù)inner都會(huì)被重新定義。

          閉包用途:

          用途1
          用途2

           

           


          裝飾器需要掌握知識(shí)

          • 作用域知識(shí)(LEGB)
          • 函數(shù)知識(shí)

            • 函數(shù)名可以作為參數(shù)輸入

            • 函數(shù)名可以作為返回值
          • 閉包
            • 在內(nèi)部函數(shù)里,對(duì)外部作用域的變量引用,那么內(nèi)部函數(shù)就是一個(gè)閉包

          寫代碼要遵循開(kāi)發(fā)封閉原則,雖然在這個(gè)原則是用的面向?qū)ο箝_(kāi)發(fā),但是也適用于函數(shù)式編程,簡(jiǎn)單來(lái)說(shuō),它規(guī)定已經(jīng)實(shí)現(xiàn)的功能代碼不允許被修改,但可以被擴(kuò)展,即:

          • 封閉:已實(shí)現(xiàn)的功能代碼塊

          • 開(kāi)放:對(duì)擴(kuò)展開(kāi)發(fā)

          如果需要在  函數(shù)執(zhí)行前 額外執(zhí)行其他功能的話,我們就可以用到裝飾器來(lái)實(shí)現(xiàn)~

          1
          2
          3
          4
          5
          def index():
              print("Hello !")
              return True
               
          index()

          如果我們需要在函數(shù) f1執(zhí)行前先輸出一句 Start ,函數(shù)執(zhí)行后輸出一句 End  ,那么我們可以這樣做:

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          def outer(func):
              def inner():
                  print("Start")
                  result = func()
                  print("End")
                  return result
              return inner
          @outer     # index = outer(index)
          def index():
              print("Hello !")
              return True
          result = index()
            
          # Start
          # Hello !
          # End

          當(dāng)寫完這段代碼后(函數(shù)未被執(zhí)行、未被執(zhí)行、未被執(zhí)行),python解釋器就會(huì)從上到下解釋代碼,步驟如下:

          1.先把 def outer(func) 函數(shù)加載到內(nèi)存

          2.執(zhí)行@outer

          執(zhí)行@outer 時(shí) , 先把 index 函數(shù) 加載到內(nèi)存 ! 并且內(nèi)部會(huì)執(zhí)行如下操作: 

          1.執(zhí)行 outer 函數(shù),將 index 作為參數(shù)傳遞   ( 此時(shí) func = index )

          2.將 outer 函數(shù)返回值 ( return inner ),重新賦值給 index  (index = inner)

          然后執(zhí)行下面的 result = index()  => 相當(dāng)于 執(zhí)行了 inner() 函數(shù)!!!


           

          問(wèn)題:如果被裝飾的函數(shù)如果有參數(shù)呢?

          一個(gè)參數(shù)
          兩個(gè)參數(shù)

          問(wèn)題:可以裝飾具有處理n個(gè)參數(shù)的函數(shù)的裝飾器嗎?

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          24
          25
          26
          27
          def outer(func):
              def inner(*args,**kwargs):
                  print("start")
                  result = func(*args,**kwargs)
                  print("end")
                  return result
              return inner
          @outer
          def index1(a1,):
              print(a1)
              return True
          @outer
          def index2(a1,a2):
              print(a1,a2)
              return True
          @outer
          def index3(a1,a2,a3):
              print(a1,a2,a3)
              return True
          index1(5)
          index2(5,6)
          index3(5,6,7)

           

          多層裝飾器

          問(wèn)題:一個(gè)函數(shù)可以被多個(gè)裝飾器裝飾嗎?

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          def outer_0(func):
              def inner(*args,**kwargs):
                  print("0.5")
                  result = func(*args,**kwargs)
                  return result
              return inner
          def outer_1(func):
              def inner(*args,**kwargs):
                  print("123")
                  result = func(*args,**kwargs)
                  print("456")
                  return result
              return inner
          @outer_0
          @outer_1
          def index(a1,a2):
              print(a1,a2)
              return True
          index(1,2)

          1.先把 outer_0 、outer_1 和 index 加載到內(nèi)存

          2.執(zhí)行 @outer_0 時(shí), func = @outer_1 和 index 函數(shù)的結(jié)合體

          3.然后執(zhí)行 @outer_0 的 inner , 其中包含了 @outer_1 和index  =>  所以先執(zhí)行 @outer_1

          4.當(dāng)執(zhí)行 @outer_1 時(shí), func = index , 并執(zhí)行 inner -> 再執(zhí)行 func  即 index 函數(shù)

          (原理同 只有一個(gè)裝飾器一樣 , 可以 把 @outer_1 和 def index 看成一個(gè) 結(jié)成的函數(shù)  => 當(dāng)作只有一個(gè) 裝飾器@outer_0)

           

          帶參數(shù)的裝飾器

            裝飾器還有更大的靈活性,例如帶參數(shù)的裝飾器:在上面的裝飾器調(diào)用中,比如@show_time,

            該裝飾器唯一的參數(shù)就是執(zhí)行業(yè)務(wù)的函數(shù)。

            裝飾器的語(yǔ)法允許我們?cè)谡{(diào)用時(shí),提供其它參數(shù),比如@decorator(a)。這樣,就為裝飾器的編寫和使用提供了更大的靈活性。

          import timedef time_logger(flag=0):     def show_time(func):             def wrapper(*args,**kwargs):                start_time=time.time()                func(*args,**kwargs)                end_time=time.time()                print('spend %s'%(end_time-start_time))                 if flag:                    print('將這個(gè)操作的時(shí)間記錄到日志中')            return wrapper    return show_time @time_logger(3)def add(*args,**kwargs):    time.sleep(1)    sum=0    for i in args:        sum+=i    print(sum) add(2,7,5)

            @time_logger(3) 做了兩件事:

           ?。?)time_logger(3):得到閉包函數(shù)show_time,里面保存環(huán)境變量flag

           ?。?)@show_time   :add=show_time(add)

            上面的time_logger是允許帶參數(shù)的裝飾器。它實(shí)際上是對(duì)原有裝飾器的一個(gè)函數(shù)封裝,并返回一個(gè)裝飾器(一個(gè)含有參數(shù)的閉包函數(shù))。

            當(dāng)我們使用@time_logger(3)調(diào)用的時(shí)候,Python能夠發(fā)現(xiàn)這一層的封裝,并把參數(shù)傳遞到裝飾器的環(huán)境中。

           

          類裝飾器

            再來(lái)看看類裝飾器,相比函數(shù)裝飾器,類裝飾器具有靈活度大、高內(nèi)聚、封裝性等優(yōu)點(diǎn)。

            使用類裝飾器還可以依靠類內(nèi)部的__call__方法,當(dāng)使用 @ 形式將裝飾器附加到函數(shù)上時(shí),就會(huì)調(diào)用此方法。

          import timeclass Foo(object):    def __init__(self, func):        self._func = func    def __call__(self):        start_time=time.time()        self._func()        end_time=time.time()        print('spend %s'%(end_time-start_time))@Foo  #bar=Foo(bar)def bar():    print ('bar')    time.sleep(2)bar()    #bar=Foo(bar)()>>>>>>>沒(méi)有嵌套關(guān)系了,直接active Foo的 __call__方法

          注意 :

            使用裝飾器極大地復(fù)用了代碼,但是他有一個(gè)缺點(diǎn)就是原函數(shù)的元信息不見(jiàn)了,比如函數(shù)的docstring、__name__、參數(shù)列表,:

            我們有functools.wraps,wraps本身也是一個(gè)裝飾器,它能把原函數(shù)的元信息拷貝到裝飾器函數(shù)中,這使得裝飾器函數(shù)也有和原函數(shù)一樣的元信息了。

          from functools import wraps def logged(func):     @wraps(func)    def wrapper(*args, **kwargs):        print (func.__name__ + " was called")        return func(*args, **kwargs)    return wrapper @loggeddef cal(x):   return x + x * x print(cal.__name__)         #cal

           

          本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
          打開(kāi)APP,閱讀全文并永久保存 查看更多類似文章
          猜你喜歡
          類似文章
          12步輕松搞定python裝飾器
          12個(gè)步驟教你理解Python裝飾器
          python基礎(chǔ)7(函數(shù) Ⅱ)
          變量作用域
          Python裝飾器decorator用法實(shí)例
          一文看懂Python系列之裝飾器(decorator)(工作面試必讀)
          更多類似文章 >>
          生活服務(wù)
          分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
          綁定賬號(hào)成功
          后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
          如果VIP功能使用有故障,
          可點(diǎn)擊這里聯(lián)系客服!

          聯(lián)系客服