python3 基礎(4)-裝飾器、生成器、迭代器

裝飾器(本質:函數)

  1. 定義:爲其他函數添加附加功能(裝飾器對於被裝飾函數是“透明的”、“不存在的”);執行過程:調用被裝飾函數實際上是調用修飾函數

  2. 原則:

    1. 不能修改被裝飾函數的源代碼;

    2. 不能修改被裝飾函數的調用方式。

  3. 實現裝飾器的知識補充:

    1. 函數即“變量”:

      1. 調用前先定義(即要先有內存地址空間、函數的執行順序)

      2. 可賦值給其他函數名

        clipboard.png

        def bar():

           print("in the bar")

        #函數bar()在以下三處位置,只有1、2處時,調用foo()才正確執行

        ------------------------------

        #1#

        def foo():

           print("in teh foo")

           bar()

        #2#

        foo()

        #3#

    2. 高階函數

      1. 條件一:一個函數(被裝飾的函數)當作實參傳給另一個函數(裝飾函數);(滿足原則一不修改被裝飾函數的源代碼)

      2. 條件二:返回值中包含函數名(裝飾函數)。(滿足原則二不修改被裝飾函數的調用方式)

    3. 函數嵌套

  4. 舉例:

    1. 統計運行時間的裝飾器

      帶無參數、固定參數、無固定參數的函數

      print("分割線".center(50,"="))

      import time

      def timmer(func):

         def wrapper(*args,**kwargs):

             start_time=time.time()

             res = func(*args,**kwargs)   #這裏的參數由wrapper函數的參數傳遞

             stop_time=time.time()

             print("func run time is %s"%(stop_time-start_time))

             return res            #返回被裝飾函數的返回值

         return wrapper   #返回裝飾函數wrapper的內存地址

      @timmer  #同test1=timmer(test1);

                 # 理解:執行timmer(test1),得到返回值wrapper內存地址,再把wrapper內存地址賦值給test1

      def test1():

         time.sleep(1)

         print("my name is test1")

         return "test1"

      @timmer   #同test2=timmer(test2);

                 # 理解:執行timmer(test2),得到返回值wrapper內存地址,再把wrapper內存地址賦值給test2

      def test2(name,age):

         time.sleep(1)

         print("my name is %s,I'm %s" %(name,age))

      test1()   #要配合上面@timmer使用,同timmer(test1)()

      print(test1())

      test2("chen",40)   #要配合上面@timmer使用,同timmer(test2)("chen",40)

      print(test2("chen",40))

      #結果

      my name is test1

      func run time is 1.000598430633545

      my name is test1

      func run time is 1.0012288093566895

      test1

      my name is chen,I'm 40

      func run time is 1.0007030963897705

      my name is chen,I'm 40

      func run time is 1.0007030963897705

      None

    2. 頁面登錄認證

      無參裝飾器:

      有參裝飾器:

      user,passwd = "chen","123456"

      def auth(func):

         def wrapper(*args,**kwargs):

             username = input("Username: ").strip()

             password = input("Password: ").strip()

             if user == username and passwd == password:

                 print("\033[32;1m通過本地認證!\033[0m")

                 return func(*args,**kwargs)

             else:

                 exit("\033[31;1m錯誤的用戶名或密碼\033[0m")

         return wrapper

      def index():

         print("welcome to index page")

      @auth

      def home():

         print("welcome to home page")

         return "from home"

      @auth

      def bbs():

         print("welcome to bbs page")

         return "from bbs"

      index()

      home()

      bbs()

      #結果

      welcome to index page

      Username: chen

      Password: 123456

      通過本地認證!

      welcome to home page

      Username: chen

      Password: 123456

      通過本地認證!

      welcome to bbs page

      user,passwd = "chen","123456"

      def auth(auth_type):

         def outer_wrapper(func):

             def wrapper(*args,**kwargs):

                 if auth_type == "local":

                     username = input("Username: ").strip()

                     password = input("Password: ").strip()

                     if user == username and passwd == password:

                         print("\033[32;1m通過本地認證!\033[0m")

                         return func(*args,**kwargs)

                     else:

                         exit("\033[31;1m錯誤的用戶名或密碼\033[0m")

                 elif auth_type == "ldap":

                     print("\033[32;1m遠程認證!\033[0m")

             return wrapper

         return outer_wrapper

      def index():

         print("welcome to index page")

      @auth(auth_type="local")   #同home=auth(auth_type="local")(home)

      def home():

         print("welcome to home page")

         return "from home"

      @auth(auth_type="ldap")   #同bbs=auth(auth_type="ldap")(bbs)

      def bbs():

         print("welcome to bbs page")

         return "from bbs"

      index()

      home()

      bbs()

      #結果

      welcome to index page

      Username: chen

      Password: 123456

      通過本地認證!

      welcome to home page

      遠程認證!

生成器 

    創建一個包含100萬個元素的列表,不僅佔用很大的存儲空間,如果我們僅僅需要訪問前面幾個元素,那後面絕大多數元素佔用的空間都白白浪費了。

    而生成器節省大量的空間,因爲生成器只有在調用時纔會生成相應的數據。

  1. 知識補充:

    1. 列表生成式(列表解析):

      a = [i*2 for i in range(10)]

      print(a)

      輸出:

      [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

    2. 斐波那契數列:

      def fib(max):

         n, a, b = 0, 0, 1

         while n < max:

             print(b)

             a, b = b, a + b

             n = n + 1

         return 'done'

      fib(10)

      輸出:

      1 1 2 3 5 8 13 21 34 55

  2. 生成器特點:

    1. 生成器只有在調用時纔會生成相應的數據

    2. 只記錄當前位置,用到哪記錄到哪

    3. 只有一個方法__next__(),python2 爲next()

  3. 生成器的實現:

    1. 方法一:列表生成式。

      a = ( i*2 for i in range(10))

      print(a)

      輸出:

      <generator object <genexpr> at 0x054D1F30>

    2. 方法二:在函數定義中包含yield關鍵字。

    3. 這時,這個函數就不再是一個普通函數,首次調用__next__()的時候執行生成器函數,遇到yield語句時返回,再次執行(__next__()或send()或for、while等)時將從上次返回的yield語句處繼續執行。

      def fib(max):

         n, a, b = 0, 0, 1

         while n < max:

             # print(b)

             yield b

             a, b = b, a + b

             n = n + 1

         return "done"

      fib(5)

      print(fib(5))   #此時不會獲取return的值

      輸出:

      <generator object fib at 0x04F8AF90>


  4. 獲取生成器的值:

    1. 使用__next__()

      print(a.__next__())

      print(a.__next__())

      print(a.__next__())

      f = fib(5)

      print(f.__next__())

      print(f.__next__())

    2. for、while循環

      f = fib(5)

      for i in f:

          print(i)

  5. 應用:

    1. 當使用__next__()獲取生成器的值的數量超過總的數量時:

      def fib(max):

         n, a, b = 0, 0, 1

         while n < max:

             # print(b)

             yield b

             a, b = b, a + b

             n = n + 1

         return "done"   #作爲錯誤提示信息

      f = fib(100)

      #當獲取生成器的值的數量超過總的數量時會報錯

      while True:

          x = f.__next__()

          print('f:', x)

      #解決方式:捕獲StopIteration錯誤

      while True:

          try:

              x = f.__next__()

              print('f:', x)

          except StopIteration as e:

              print('Generator return value:', e.value)

              break

    2. 在單線程實現併發運算的效果(攜程??)

      補充:send()用於給yield傳值,但是send傳值時,要求生成器已執行到yield語句處(就是send前面至少要有一個__next__(),這樣才能保證生成器運行到yield處

      import time

      def consumer(name):

         print("%s 準備吃包子啦!" %name)

         while True:

             baozi = yield   #這裏的yield由send傳值

             print("[%s]包子來了,被[%s]吃了!" %(baozi,name))

      def producer(name):

         c = consumer('A')

         c2 = consumer('B')

         c3 = consumer('C')

         c.__next__()

         c2.__next__()

         c3.__next__()

         print("師傅開始蒸包子啦!")

         for i in ["豬肉餡","韭菜餡","白菜餡","豆沙餡"]:

             time.sleep(1)

             print("%s包子出爐了!"%i)

             c.send(i)

             c2.send(i)

             c3.send(i)

      producer("alex")

迭代器

  1. Iterable對象:可以直接作用於for循環的對象統稱爲可迭代對象

    1. 集合數據類型,如list、tuple、dict、set、str等;

    2. 生成器generator,包括帶yield的generator function。

    3. 內置函數:map()、filter()、zip(a,b)

  2. Iterator對象:可以被next()函數調用並不斷返回下一個值的對象稱爲迭代器對象

    1. 生成器generator

    2. 內置函數:map()、filter()、zip(a,b)

  3. 判斷一個對象是否是Iterable對象:

    #使用isinstance()

    >>> from collections import Iterable

    >>> isinstance([], Iterable)

    True

    >>> isinstance({}, Iterable)

    True

    >>> isinstance('abc', Iterable)

    True

    >>> isinstance((x for x in range(10)), Iterable)

    True

    >>> isinstance(100, Iterable)

    False


  4. 判斷一個對象是否是Iterator對象:

    #使用isinstance()

    >>> from collections import Iterator

    >>> isinstance((x for x in range(10)), Iterator)

    True

    >>> isinstance([], Iterator)

    False

    >>> isinstance({}, Iterator)

    False

    >>> isinstance('abc', Iterator)

    False


  5. 把Iterable對象變成Iterator對象:

    #使用iter()函數:

    >>> isinstance(iter([]), Iterator)

    True

    >>> isinstance(iter('abc'), Iterator)

    True

    #將列表變爲iterator對象

    a = [1,2,3,4]

    b=iter(a)

    print(type(b))

    print(b.__next__())

    print(b.__next__())

    #輸出

    <class 'list_iterator'>

    1

    2


  6. 你可能會問,爲什麼list、dict、str等數據類型不是Iterator?

    1. 這是因爲Python的Iterator對象表示的是一個數據流,Iterator對象可以被next()函數調用並不斷返回下一個數據,直到沒有數據時拋出StopIteration錯誤。可以把這個數據流看做是一個有序序列,但我們卻不能提前知道序列的長度,只能不斷通過next()函數實現按需計算下一個數據,所以Iterator的計算是惰性的,只有在需要返回下一個數據時它纔會計算。

    2. Iterator甚至可以表示一個無限大的數據流,例如全體自然數。而使用list是永遠不可能存儲全體自然數的。

  7. 補充:

    1. Python的for循環本質上就是通過不斷調用next()函數實現的,例如:

      for x in [1, 2, 3, 4, 5]:

         pass

      #等價於

      it = iter([1, 2, 3, 4, 5])   # 首先獲得Iterator對象:

      while True:

         try:

             x = next(it)   # 獲得下一個值:

         except StopIteration:   # 遇到StopIteration就退出循環

             break



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章