天天看點

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

裝飾器(本質:函數)

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

原則:

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

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

實作裝飾器的知識補充:

函數即“變量”:

調用前先定義(即要先有記憶體位址空間、函數的執行順序)

可指派給其他函數名

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

<col>

def bar():

   print("in the bar")

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

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

#1#

def foo():

   print("in teh foo")

   bar()

#2#

foo()

#3#

高階函數

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

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

函數嵌套

舉例:

統計運作時間的裝飾器

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

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):

   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

func run time is 1.0012288093566895

test1

my name is chen,I'm 40

func run time is 1.0007030963897705

None

頁面登入認證

無參裝飾器:

有參裝飾器:

user,passwd = "chen","123456"

def auth(func):

       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"

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

welcome to bbs page

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

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

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

遠端認證!

生成器 

    建立一個包含100萬個元素的清單,不僅占用很大的存儲空間,如果我們僅僅需要通路前面幾個元素,那後面絕大多數元素占用的空間都白白浪費了。

    而生成器節省大量的空間,因為生成器隻有在調用時才會生成相應的資料。

知識補充:

清單生成式(清單解析):

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

print(a)

輸出:

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

斐波那契數列:

def fib(max):

   n, a, b = 0, 0, 1

   while n &lt; 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

生成器特點:

生成器隻有在調用時才會生成相應的資料

隻記錄目前位置,用到哪記錄到哪

隻有一個方法__next__(),python2 為next()

生成器的實作:

方法一:清單生成式。

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

&lt;generator object &lt;genexpr&gt; at 0x054D1F30&gt;

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

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

       # print(b)

       yield b

   return "done"

fib(5)

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

輸出:

&lt;generator object fib at 0x04F8AF90&gt;

擷取生成器的值:

使用__next__()

print(a.__next__())

f = fib(5)

print(f.__next__())

for、while循環

for i in f:

    print(i)

應用:

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

   return "done"   #作為錯誤提示資訊

f = fib(100)

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

while True:

    x = f.__next__()

    print('f:', x)

#解決方式:捕獲StopIteration錯誤

    try:

        x = f.__next__()

        print('f:', x)

    except StopIteration as e:

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

        break

在單線程實作并發運算的效果(攜程??)

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

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")

疊代器

Iterable對象:可以直接作用于for循環的對象統稱為可疊代對象

集合資料類型,如list、tuple、dict、set、str等;

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

内置函數:map()、filter()、zip(a,b)

Iterator對象:可以被next()函數調用并不斷傳回下一個值的對象稱為疊代器對象

生成器generator

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

#使用isinstance()

&gt;&gt;&gt; from collections import Iterable

&gt;&gt;&gt; isinstance([], Iterable)

True

&gt;&gt;&gt; isinstance({}, Iterable)

&gt;&gt;&gt; isinstance('abc', Iterable)

&gt;&gt;&gt; isinstance((x for x in range(10)), Iterable)

&gt;&gt;&gt; isinstance(100, Iterable)

False

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

&gt;&gt;&gt; from collections import Iterator

&gt;&gt;&gt; isinstance((x for x in range(10)), Iterator)

&gt;&gt;&gt; isinstance([], Iterator)

&gt;&gt;&gt; isinstance({}, Iterator)

&gt;&gt;&gt; isinstance('abc', Iterator)

把Iterable對象變成Iterator對象:

#使用iter()函數:

&gt;&gt;&gt; isinstance(iter([]), Iterator)

&gt;&gt;&gt; isinstance(iter('abc'), Iterator)

#将清單變為iterator對象

a = [1,2,3,4]

b=iter(a)

print(type(b))

print(b.__next__())

#輸出

&lt;class 'list_iterator'&gt;

1

2

你可能會問,為什麼list、dict、str等資料類型不是Iterator?

這是因為Python的Iterator對象表示的是一個資料流,Iterator對象可以被next()函數調用并不斷傳回下一個資料,直到沒有資料時抛出StopIteration錯誤。可以把這個資料流看做是一個有序序列,但我們卻不能提前知道序列的長度,隻能不斷通過next()函數實作按需計算下一個資料,是以Iterator的計算是惰性的,隻有在需要傳回下一個資料時它才會計算。

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

補充:

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

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

   pass

#等價于

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

   try:

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

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

       break

繼續閱讀