乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      【Python之路】特別篇

       highoo 2019-03-20
      前情提要

      1. 作用域

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

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

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

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

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

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

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

      當(dāng)然,local和enclosing是相對的,enclosing變量相對上層來說也是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)才會引入新的作用域,其它的代碼塊(如if、try、for等)是不會引入新的作用域的。

      讓我們寫一個簡單的函數(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返回一個包含所有python解釋器知道的變量名稱的字典。

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

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

      global關(guān)鍵字 

      當(dāng)內(nèi)部作用域想修改外部作用域的變量時,就要用到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作用域,外層非全局作用域)中的變量怎么辦呢,這時就需要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)然這并不是說我們在函數(shù)里面就不能訪問外面的全局變量。

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

      但是訪問或者修改變量時會先在當(dāng)前作用域查找變量,沒有找到匹配變量的話會依次向上在閉合的作用域里面進(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解釋器會嘗試查找變量a_string,當(dāng)然在函數(shù)的本地作用域里面是找不到的,所以接著會去上層的作用域里面去查找。

      但是另一方面,假如我們在函數(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'

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

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

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

      我們也能看到在#2處打印出來的變量a_string的值并沒有改變。

      3. 變量生存周期

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

      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ā)生的錯誤不僅僅是因?yàn)?code>作用域規(guī)則導(dǎo)致的(盡管這是拋出了NameError的錯誤的原因)

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

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

      函數(shù)foo的命名空間隨著函數(shù)調(diào)用開始而開始,結(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解釋器需找一個叫x的本地變量,查找失敗之后會繼續(xù)在上層的作用域里面尋找。

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

      對函數(shù)outer來說,變量x是一個本地變量,但是如先前提到的一樣,

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

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

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

      5. 閉包

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

      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作為一個函數(shù)被outer返回,保存在一個變量foo,并且我們能夠?qū)λM(jìn)行調(diào)用foo()。

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

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

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

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

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

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

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

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

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

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

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

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

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

      閉包用途:

      用途1
      用途2

       

       


      裝飾器需要掌握知識

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

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

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

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

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

      • 開放:對擴(kuò)展開發(fā)

      如果需要在  函數(shù)執(zhí)行前 額外執(zhí)行其他功能的話,我們就可以用到裝飾器來實(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解釋器就會從上到下解釋代碼,步驟如下:

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

      2.執(zhí)行@outer

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

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

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

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


       

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

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

      問題:可以裝飾具有處理n個參數(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)

       

      多層裝飾器

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

      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 時, func = @outer_1 和 index 函數(shù)的結(jié)合體

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

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

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

       

      帶參數(shù)的裝飾器

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

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

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

      復(fù)制代碼
      import time
      def 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('將這個操作的時間記錄到日志中')
      
                  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)
      復(fù)制代碼

        @time_logger(3) 做了兩件事:

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

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

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

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

       

      類裝飾器

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

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

      復(fù)制代碼
      import time
      class 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)()>>>>>>>沒有嵌套關(guān)系了,直接active Foo的 __call__方法
      復(fù)制代碼

      注意 :

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

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

      復(fù)制代碼
      from functools import wraps
       
      def logged(func):
       
          @wraps(func)
          def wrapper(*args, **kwargs):
              print (func.__name__ + " was called")
              return func(*args, **kwargs)
          return wrapper
       
      @logged
      def cal(x):
         return x + x * x
       
      print(cal.__name__)         #cal
      復(fù)制代碼

       

        本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多