首頁 > 軟體

python中的生成器、迭代器、裝飾器詳解

2022-07-08 14:03:23

一、裝飾器

由於一個函數能實現一種功能,現在想要在不改變其程式碼的情況下,讓這個函數進化一下,即能保持原來的功能,還能有新的"技能",怎麼辦?

現已經存在一個自定義的函數func1

def func1():
    print('hello,world!')

讓func1進化一下:(繼承func1之前的所有功能,而且還有新的‘技能’)

效果和下面定義的函數func2效果是一樣的

def func2():
    func1() #呼叫func1,即可保持func1這一函數的所有的功能都被這個新的函數繼承下來
    print('hello,boy!') #新增的新功能,相當於func1這一函數學到的新技能

但是,func2是一個新的函數,已經完全改頭換面了,雖然有一部分是能實現func1的功能,但並不是func1的進化型,所以當我們還想用呼叫func1這個函數的呼叫方法呼叫的時候,並不能呼叫func2.

當函數不進行呼叫時候,可以將這個函數當成一個變數進行對待。所以,如果把func2這個函數的記憶體地址賦給func1,然後呼叫func1,那麼就能用func1這個名字,呼叫func2這個函數,也就實現了func1這個函數的進化。

所以,如果定義如下一個可以實現上述功能的函數deco,deco這個函數就要完成以下的兩件事情:

1.讓func2這個函數的記憶體地址出現,即:定義func2這個函數

2.在其執行之後能夠,拿到func2的記憶體地址,即把func2的記憶體地址作為返回值返回

def deco(func1):
    def func2():
        func1() #呼叫func1,即可保持func1這一函數的所有的功能都被這個新的函數繼承下來
        print('hello,boy!') #新增的新功能,相當於func1這一函數學到的新技能
    return func2

完成上述deco函數的定義之後,當執行deco這個函數之後,其執行結果,就是func2的記憶體地址。

接下來,將這個記憶體地址賦值給func1這個變數之後,再對func1進行呼叫,就可以完成對函數func1的"進化"(即:在不改變func1的程式碼,還賦予了其新的功能)

func1 = deco(func1)
func1()

上述的過程可以用以下的程式碼進行實現:

def deco(func1):
    def func2():
        func1() #呼叫func1,即可保持func1這一函數的所有的功能都被這個新的函數繼承下來
        print('hello,boy!') #新增的新功能,相當於func1這一函數學到的新技能
    return func2
 
def func1():
    print('hello,world!')
     
func1 = deco(func1)
func1()

其中,deco這個函數就是所謂的裝飾器

(裝飾器:在不改變原始碼和呼叫方式的基礎之上給函數增加新的功能)

將上述程式碼進行優化之後就有了下面的程式碼:

def deco(func1):
    def func2():
        func1() #呼叫func1,即可保持func1這一函數的所有的功能都被這個新的函數繼承下來
        print('hello,boy!') #新增的新功能,相當於func1這一函數學到的新技能
    return func2
 
@deco #效果等同於func1=deco(func1)
def func1():
    print('hello,world!')
 
func1()

1.1含引數的裝飾器:

def deco(func):
    def wrapper(username,password):
        if username == 'root' and password == 'root':
            func(username,password)  else:
            print('使用者名稱或密碼錯誤')
    return wrapper
 
@deco
def baidu_index(username,password):
    print('welcome to 百度')
 
baidu_index('root','root')

由於定義的函數baidu_index,必須要傳遞引數,所以裝飾器內部定義的函數wrapper也需要定義形參,wrapper函數內部呼叫函數時,也需要有引數!!

1.2多層裝飾器

將裝飾器1看成一個整體,在這個裝飾器上在新增一個裝飾器2,就能實現..........

例如:

def deco1(deco):
    print('你好不好?')
    def deco(func):
        def func2():
            print('你不好!')
            func()
        return func2
    return deco
 
@deco1
def deco(func):
    def func2():
        print('你不好!')
        func()
    return func2
 
@deco
def func1():
    print('你好!')
 
func1()

二、迭代器:

1.什麼是迭代?

1.迭代是一個重複的過程,即每一次重複為一次迭代,

2.並且每次迭代的結果都是下一次迭代的初始值

例如:

l = [1,2,3]
count=0
while count<len(l): #首先是重複動作,其次上一次的結果是下一次的初始值,因此,是迭代
  print(l[count])
  count+=1

2.什麼是迭代器?為何要有迭代器?

對於序列型別:字串、列表、元組,我們可以使用索引的方式迭代取出其包含的元素。但對於字典、集合、檔案等型別是沒有索引的,若還想取出其內部包含的元素,則必須找出一種不依賴於索引的迭代方式,這就是迭代器。

3.什麼叫做迭代器物件?

obj有.__iter__和.__next__方法的叫做迭代器物件

總結:迭代器物件一定是可迭代物件,而可迭代物件不一定是迭代器物件

4.for的作用:

1.把可迭代物件變成迭代器物件

2.過濾錯誤資訊

l1 = [1,2,3]
for i in l1: #iter(l1)
  print(i)

三、生成器

1.什麼是生成器?

只要函數裡有yield關鍵字,那麼函數名()得到的結果就是生成器,生成器就是迭代器,並且不會執行函數內部程式碼

2.return和yield用法十分類似,但是也有區別,區別在於:return只能返回一個值,而yield可以返回多個值

3.生成器優點:

同一時間只儲存一個值,節省記憶體空間

4.生成器的缺點:

只能向後取值,不能往前取值

def test():
  for i in range(100):
    yield i
 
res = test()
 
for k in res:
  print(k)

四、總結

迭代器

迭代是Python最強大的功能之一,是存取集合元素的一種方式;迭代器是一個可以記住遍歷的位置的物件;迭代器物件從集合的第一個元素開始存取,直到所有的元素被存取完結束。迭代器只能往前不會後退;迭代器有兩個基本的方法:iter()和next();字串,列表或元組物件都可用於建立迭代器:

迭代器python範例

生成器

在 Python 中,使用了 yield 的函數被稱為生成器;跟普通函數不同的是,生成器是一個返回迭代器的函數,只能用於迭代操作,更簡單點理解生成器就是一個迭代器;在呼叫生成器執行的過程中,每次遇到 yield 時函數會暫停並儲存當前所有的執行資訊,返回 yield 的值, 並在下一次執行 next() 方法時從當前位置繼續執行;呼叫一個生成器函數,返回的是一個迭代器物件。

使用生成器生成斐波那些數列

裝飾器

裝飾器:在不改變原函數的基礎上,對函數執行前後進行自定義操作。把目標函數作為引數傳給裝飾器函數,裝飾器函數執行過程中,執行目標函數,達到在目標函數執行前後進行自定義操作的目的。

應用場景:如記錄函數執行時間;flask裡的路由、before_request;django中的快取、使用者登入等。

使用裝飾器記錄函數執行時間

裝飾器在實現的時候,被裝飾後的函數其實已經是另外一個函數了(函數名等函數屬性會發生改變),為了不影響,Python的functools包中提供了一個叫wraps的裝飾器來消除這樣的副作用。寫一個裝飾器的時候,最好在實現之前加上functools的wrap,它能保留原有函數的名稱和檔案字串。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com