日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

[转载] python迭代器、生成器和装饰器

發布時間:2025/3/11 python 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [转载] python迭代器、生成器和装饰器 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

參考鏈接: 有效地在Python中使用迭代

文章目錄

?生成器生成器表達式(generator expression)通過使用yield關鍵字定義生成器并行前戲高潮

??

? 迭代器迭代器概述iter()函數 創建迭代器創建一個迭代器(類)內置迭代器工具count無限迭代器cycle 無限迭代器,從一個有限序列中生成無限序列:itertools的子模塊 islice 控制無限迭代器輸出的方式

??

? 裝飾器高階函數嵌套函數高階函數+嵌套函數 = 裝飾器類裝飾器帶參數的decorator實例---登錄認證

?

?

?

生成器?

通過列表生成式,我們可以直接創建一個列表。但是,受到內存限制,列表容量肯定是有限的。而且,創建一個包含100萬個元素的列表,不僅占用很大的存儲空間,如果我們僅僅需要訪問前面幾個元素,那后面絕大多數元素占用的空間都白白浪費了。?

所以,如果列表元素可以按照某種算法推算出來,那我們是否可以在循環的過程中不斷推算出后續的元素呢?這樣就不必創建完整的list,從而節省大量的空間。在Python中,這種一邊循環一邊計算的機制,稱為生成器:generator。?

要創建一個generator,有很多種方法。第一種方法很簡單,只要把一個列表生成式的[]改成(),就創建了一個generator:?

生成器表達式(generator expression)?

L = [x + 1 for x in range(10)]

print(L)

?

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

?

列表生成式復習?

實現列表元素加1,列表生成式與其它方法比較:?

#普通方法1

b = []

for i in range(10):

? ? b.append(i+1)

print(b)

?

#普通方法2

a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

for index,i in enumerate(a):

? ? a[index] +=1

print(a)

?

#map,lambda

a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

a = map(lambda x:x+1, a)

print(list(a))

?

?

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

?

g = (x + 1 for x in range(10))

print(g)

?

<generator object <genexpr> at 0x7fe03ad859a8>

?

創建L和g的區別僅在于最外層的[]和(),L是一個list,而g是一個generator。?

我們可以直接打印出list的每一個元素,但我們怎么打印出generator的每一個元素呢??

如果要一個一個打印出來,可以通過next()函數(or __next__())獲得generator的下一個返回值:?

next(g)

?

1

?

next(g)

?

2

?

next(g)

?

3

?

g.__next__()

?

4

?

g.__next__()

?

5

?

generator保存的是算法,每次調用next(g),就計算出g的下一個元素的值,直到計算到最后一個元素,沒有更多的元素時,拋出StopIteration的錯誤?

上面這種不斷調用next(g)實在是太變態了,正確的方法是使用for循環,因為generator也是可迭代對象:?

g = (x * x for x in range(10))

for n in g:

? ? print(n,end=";")

?

0;1;4;9;16;25;36;49;64;81;

?

所以,我們創建了一個generator后,基本上永遠不會調用next(),而是通過for循環來迭代它,并且不需要關心StopIteration的錯誤?

通過使用yield關鍵字定義?

生成器對象是通過使用yield關鍵字定義的函數對象,因此,生成器也是一個函數。生成器用于生成一個值得序列,以便在迭代器中使用。?

"""

第一是直接作為腳本執行,

第二是import到其他的python腳本中被調用(模塊重用)執行。

因此if __name__ == '__main__': 的作用就是控制這兩種情況執行代碼的過程,

在if __name__ == '__main__':下的代碼只有在第一種情況下(即文件作為腳本直接執行)才會被執行,而import到其他腳本中是不會被執行的。

"""

?

def myYield(n):

? ? while n>0:

? ? ? ? print('開始生成。。。')

? ? ? ? yield n

? ? ? ? print('完成一次。。。')

? ? ? ? n -= 1

if __name__ == '__main__':

? ? a = myYield(3)

? ? print('已經實例化生成器對象')

#? ? ?a.__next__()

#? ? ?print('第二次調用__next__()方法:')

#? ? ?a.__next__()

?

已經實例化生成器對象

?

yield 語句是生成器中的關鍵語句,生成器在實例化時并不會被執行,而是等待調用其__next__()方法才開始運行。并且當程序運行完yield語句后就會“吼(hold)住”,即保持當前狀態且停止運行,等待下一次遍歷時才恢復運行。?

程序運行的結果中的空行后的輸出“已經實例化生成器對象”之前,已經實例化了生成器對象,但生成器并沒有運行(沒有輸出‘開始生成’)。當第一次手工調用__next__()方法之后,才輸出‘開始生成’,標志著生成器已經運行,而在輸出‘’第二次調用__next__()方法‘’之前并沒有輸出‘完成一次’,說明yield語句運行之后就立即停止了。而第二次調用__next__()方法之后,才輸出‘完成一次’,說明生成器的回復運行是從yield語句之后開始運行的?

return_value = a.__next__()

print(return_value)

?

開始生成。。。

3

?

print('第二次調用__next__()方法:')

?

第二次調用__next__()方法:

?

return_value = a.__next__()

print(return_value)

?

完成一次。。。

開始生成。。。

2

?

著名的斐波拉契數列(Fibonacci),除第一個和第二個數外,任意一個數都可由前兩個數相加得到:?

斐波拉契數列用列表生成式寫不出來,但是,用函數把它打印出來卻很容易:?

def fib(max):

? ? n, a, b = 0, 0, 1

? ? while n < max:

? ? ? ? print(b)

? ? ? ? a, b = b, a + b

? ? ? ? n = n + 1

? ? return 'done'

?

注意,賦值語句:?

a, b = b, a + b

?

相當于:?

t = (b, a + b) # t是一個tuple

a = t[0]

b = t[1]

?

上面的函數可以輸出斐波那契數列的前N個數:?

fib(5)

?

1

1

2

3

5

?

?

?

?

?

'done'

?

上面的函數和generator僅一步之遙。要把fib函數變成generator,只需要把print(b)改為yield b就可以了:?

def fib(max):

? ? n,a,b = 0,0,1

? ? while n < max:

? ? ? ? #print(b)

? ? ? ? yield? b

? ? ? ? a,b = b,a+b

? ? ? ? n += 1

? ? return 'well done'?

?

這里,最難理解的就是generator和函數的執行流程不一樣。函數是順序執行,遇到return語句或者最后一行函數語句就返回。而變成generator的函數,在每次調用next()的時候執行,遇到yield語句返回,再次執行時從上次返回的yield語句處繼續執行。?

f = fib(5)

print(f)

print(list(f))

?

#重新實例化生成器對象

f = fib(5)

?

<generator object fib at 0x7fe038493840>

[1, 1, 2, 3, 5]

?

print(f.__next__())

print(f.__next__())

print("干點別的事")

print(f.__next__())

print(f.__next__())

print(f.__next__())

print(f.__next__())

?

1

1

干點別的事

2

3

5

?

?

?

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

?

StopIteration? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Traceback (most recent call last)

?

<ipython-input-18-9609f54647c6> in <module>

? ? ? 5 print(f.__next__())

? ? ? 6 print(f.__next__())

----> 7 print(f.__next__())

?

?

StopIteration: well done

?

用for循環調用generator時,發現拿不到generator的return語句的返回值。如果想要拿到返回值,必須捕獲StopIteration錯誤,返回值包含在StopIteration的value中:?

g = fib(6)

while True:

? ? try:

? ? ? ? x = next(g)

? ? ? ? print('g:', x)

? ? except StopIteration as e:

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

? ? ? ? break

?

g: 1

g: 1

g: 2

g: 3

g: 5

g: 8

Generator return value: well done

?

from itertools import islice

def fib():

? ? a,b = 0,1

? ? while True:

? ? ? ? yield b

? ? ? ? a,b = b,a+b

f = fib()

print(list(islice(f ,0,10)))

?

[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

?

生成器在Python中是一個非常強大的編程結構,可以用更少地中間變量寫流式代碼,此外,相比其它容器對象它更能節省內存和CPU,當然它可以用更少的代碼來實現相似的功能。現在就可以動手重構你的代碼了,但凡看到類似?

def something():

? ? result= []

? ? for ... in ...:

? ? ? ? result.append(x)

? ? ? ? return result

?

都可以用生成器函數來替換:?

def iter_something():

? ? result = []

? ? for ... in ...:

? ? ? ? yield x

?

楊輝三角?

期待輸出:?

[1]

[1, 1]

[1, 2, 1]

[1, 3, 3, 1]

[1, 4, 6, 4, 1]

[1, 5, 10, 10, 5, 1]

[1, 6, 15, 20, 15, 6, 1]

[1, 7, 21, 35, 35, 21, 7, 1]

[1, 8, 28, 56, 70, 56, 28, 8, 1]

[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]

?

def triangles():

? ? result = [1]

? ? while True:

? ? ? ? yield result

? ? ? ? result = [1] + [result[i] + result[i+1] for i in range(len(result)-1)] + [1]

?

n = 0

results = []

for t in triangles():

? ? print(t)

? ? results.append(t)

? ? n = n + 1

? ? if n == 10:

? ? ? ? break

if results == [

? ? [1],

? ? [1, 1],

? ? [1, 2, 1],

? ? [1, 3, 3, 1],

? ? [1, 4, 6, 4, 1],

? ? [1, 5, 10, 10, 5, 1],

? ? [1, 6, 15, 20, 15, 6, 1],

? ? [1, 7, 21, 35, 35, 21, 7, 1],

? ? [1, 8, 28, 56, 70, 56, 28, 8, 1],

? ? [1, 9, 36, 84, 126, 126, 84, 36, 9, 1]

]:

? ? print('測試通過!')

else:

? ? print('測試失敗!')

?

?

[1]

[1, 1]

[1, 2, 1]

[1, 3, 3, 1]

[1, 4, 6, 4, 1]

[1, 5, 10, 10, 5, 1]

[1, 6, 15, 20, 15, 6, 1]

[1, 7, 21, 35, 35, 21, 7, 1]

[1, 8, 28, 56, 70, 56, 28, 8, 1]

[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]

測試通過!

?

生成器并行?

前戲?

def gen():

? ? a = yield 1

? ? print('yield a % s' % a)

? ? b = yield 2

? ? print('yield b % s' % b)

? ? c = yield 3

? ? print('yield c % s' % c)

? ? return "happy ending"

?

?

r = gen()

x = next(r)

print('next x %s' % x)

y = r.send(10)

print('next y %s' %y)

z = next(r)

print('next z %s' % z)

try:

? ? a = next(r)

except StopIteration as e:

? ? print(e)

?

?

next x 1

yield a 10

next y 2

yield b None

next z 3

yield c None

happy ending

?

運行過程說明:?

第一步:r = gen(),實例化一個生成器對象第二步:調用next() ,遇到yield 暫停,返回值1,賦值給x第三步:打印x的值第四步:傳值10,在暫停處接受值10,賦值給a,繼續運行,打印a的值,遇到第二個yield,暫停,返回值2,賦值給y第五步:打印y的值第六步:調用next() ,打印b值,遇到第三個yield暫停,返回值3,賦值給z第七步:打印z值第八步:調用next(),打印c的值,報StopIteration錯誤,用try。。。except捕獲錯誤?

高潮?

import time

import random

?

food = ["韭菜雞蛋","豬肉白菜","豬肉薺菜","羊肉白菜","豬肉大蔥","蝦仁海鮮"]

?

?

def consumer(name):

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

? ? while True:

? ? ? ? baozi = yield 'n'

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

?

? ? ? ??

def producer(name):

? ? c1 = consumer('大兒子')

? ? c2 = consumer('小兒子')

? ? c1.__next__()

? ? c2.__next__()

? ? print("%s開始準備做包子啦" % name)

? ? for i in range(6):

? ? ? ? print("第%d次做了%s個包子"%(i+1,len(food)))

? ? ? ? time.sleep(random.randint(1,3))

? ? ? ? f1 = food[i]

? ? ? ? c1.send(f1)

? ? ? ? food.append(f1)

? ? ? ? random.shuffle(food)

? ? ? ? c2.send(food[i])

? ? ? ??

producer('老子')

?

大兒子 準備吃包子啦!

小兒子 準備吃包子啦!

老子開始準備做包子啦

第1次做了6個包子

[韭菜雞蛋]餡包子來了,被[大兒子]吃了!

[韭菜雞蛋]餡包子來了,被[小兒子]吃了!

第2次做了7個包子

[韭菜雞蛋]餡包子來了,被[大兒子]吃了!

[豬肉大蔥]餡包子來了,被[小兒子]吃了!

第3次做了8個包子

[韭菜雞蛋]餡包子來了,被[大兒子]吃了!

[豬肉大蔥]餡包子來了,被[小兒子]吃了!

第4次做了9個包子

[豬肉白菜]餡包子來了,被[大兒子]吃了!

[羊肉白菜]餡包子來了,被[小兒子]吃了!

第5次做了10個包子

[蝦仁海鮮]餡包子來了,被[大兒子]吃了!

[韭菜雞蛋]餡包子來了,被[小兒子]吃了!

第6次做了11個包子

[韭菜雞蛋]餡包子來了,被[大兒子]吃了!

[蝦仁海鮮]餡包子來了,被[小兒子]吃了!

?

迭代器?

迭代器概述?

可以直接作用于for循環的數據類型有以下幾種:?

一類是集合數據類型,如list,tuple,dict,set,str等一類是generator ,包括生成器和帶yeild的generator function?

這些可以 直接作用于for循環的對象統稱為可迭代對象:Iterable?

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

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

next(a)

?

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

?

TypeError? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Traceback (most recent call last)

?

<ipython-input-23-8981550fe3e0> in <module>

? ? ? 1 a = [i for i in range(10)]

----> 2 next(a)

?

?

TypeError: 'list' object is not an iterator

?

list,dict,str雖然是Iterable,卻不是Iterator?

from collections import Iterator

from collections import Iterable

print(isinstance(a,Iterator))

print(isinstance(a,Iterable))

print(isinstance({},Iterable))

print(isinstance('abc',Iterable))

?

False

True

True

True

?

生成器就是一個迭代器?

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

print(next(a))

print(isinstance(a,Iterator))

?

0

True

?

iter()函數 創建迭代器?

iter(iterable)#一個參數,要求參數為可迭代的類型?

把list、dict、str等Iterable變成Iterator可以使用iter()函數:?

print(isinstance({},Iterator))

print(isinstance('abc',Iterator))

print(isinstance(iter({}),Iterator))

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

?

False

False

True

True

?

你可能會問,為什么list、dict、str等數據類型不是Iterator??

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

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

小結?

?凡是可作用于for循環的對象都是Iterable類型;? 凡是可作用于next()函數的對象都是Iterator類型,它們表示一個惰性計算的序列;? 集合數據類型如list、dict、str等是Iterable但不是Iterator,不過可以通過iter()函數獲得一個Iterator對象。??

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

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

? ? print(x,end=',')

?

1,2,3,4,5,

?

實際上完全等價于:?

# 首先獲得Iterator對象:

it = iter([1, 2, 3, 4, 5])

# 循環:

while True:

? ? try:

? ? ? ? # 獲得下一個值:

? ? ? ? x = next(it)

? ? ? ? print(x,end=',')

? ? except StopIteration:

? ? ? ? # 遇到StopIteration就退出循環

? ? ? ? break

?

?

1,2,3,4,5,

?

創建一個迭代器(類)?

把一個類作為一個迭代器使用需要在類中實現兩個方法 __iter__() 與 __next__() 。?

如果你已經了解的面向對象編程,就知道類都有一個構造函數,Python 的構造函數為 __init__(), 它會在對象初始化的時候執行?

__iter__() 方法返回一個特殊的迭代器對象, 這個迭代器對象實現了 __next__() 方法并通過 StopIteration 異常標識迭代的完成。?

from itertools import islice

class Fib:

? ? def __init__(self):

? ? ? ? self.prev = 0

? ? ? ? self.curr = 1

? ? def __iter__(self):

? ? ? ? return self

? ? def __next__(self):

? ? ? ? self.prev,self.curr = self.curr,self.prev+self.curr

? ? ? ? return self.curr

f = Fib()

print(list(islice(f ,0,10)))

?

[1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

?

Fib既是一個可迭代對象(因為它實現了 __iter__方法),又是一個迭代器(因為實現了 __next__方法)?

StopIteration?

StopIteration 異常用于標識迭代的完成,防止出現無限循環的情況,在 next() 方法中我們可以設置在完成指定循環次數后觸發 StopIteration 異常來結束迭代。?

在 20 次迭代后停止執行:?

class MyNumbers:

? ? def __init__(self):

? ? ? ? self.a = 1

? ??

? ? def __iter__(self):

? ? ? ? return self

?

? ? def __next__(self):

? ? ? ? if self.a <= 20:

? ? ? ? ? ? x = self.a

? ? ? ? ? ? self.a += 1

? ? ? ? ? ? return x

? ? ? ? else:

? ? ? ? ? ? raise StopIteration

?

myclass = MyNumbers()

myiter = MyNumbers()

# myiter = iter(myclass)

?

for x in myiter:

? ? print(x,end=",")

?

?

1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,

?

內置迭代器工具?

比如 itertools 函數返回的都是迭代器對象?

count無限迭代器?

from itertools import count

counter = count(start=10)??

print(next(counter))

print(next(counter)) #python內建函數next()對itertools創建的迭代器進行循環

?

10

11

?

cycle 無限迭代器,從一個有限序列中生成無限序列:?

from itertools import cycle

colors = cycle(['red','black','blue'])

print(next(colors))

print(next(colors))

print(next(colors))

print(next(colors))

print(next(colors))??

?

red

black

blue

red

black

?

itertools的子模塊 islice 控制無限迭代器輸出的方式?

islice的第二個參數控制何時停止迭代,迭代了11次后停止,從無限的序列中生成有限序列:?

from itertools import count

counter = count(start=10)

i=4

print(next(counter))

while i > 0:

? ? print(next(counter))

? ? i -= 1

?

10

11

12

13

14

?

from itertools import count

for i in count(10):

? ? if i > 14 :

? ? ? ? break

? ? else:

? ? ? ? print(i)

?

10

11

12

13

14

?

from itertools import islice

from itertools import count

for i in islice(count(10),5):

? ? print(i)

?

10

11

12

13

14

?

from itertools import cycle

from itertools import islice

colors = cycle(['red','black','blue'])

limited = islice(colors,0,4)

for x in limited:

? ? print(x)

?

red

black

blue

red

?

裝飾器?

器,代表函數的意思?

?裝飾器:本質是函數(裝飾其他函數)就是為其他函數添加附加功能?

每個人都有的內褲主要功能是用來遮羞,但是到了冬天它沒法為我們防風御寒,咋辦?我們想到的一個辦法就是把內褲改造一下,讓它變得更厚更長,這樣一來,它不僅有遮羞功能,還能提供保暖,不過有個問題,這個內褲被我們改造成了長褲后,雖然還有遮羞功能,但本質上它不再是一條真正的內褲了。于是聰明的人們發明長褲,在不影響內褲的前提下,直接把長褲套在了內褲外面,這樣內褲還是內褲,有了長褲后寶寶再也不冷了。裝飾器就像我們這里說的長褲,在不影響內褲作用的前提下,給我們的身子提供了保暖的功效。?

原則:?

1 不能修改被裝飾的函數的源代碼2 不能修改被裝飾的函數的調用方式?

實現裝飾器知識儲備:?

?1 函數即“”變量“”? 2 高階函數 a 把一個函數名當做實參傳給另一個函數 b 返回值中包含函數名??

高階函數?

import time

def bar():

? ? time.sleep(3)

? ? print('in the bar')

def test2(func):

? ? print(func)

? ? return func

?

print(test2(bar)) #調用test2,打印bar的內存地址,返回bar的內存地址,又打印

?

<function bar at 0x7fe03849e620>

<function bar at 0x7fe03849e620>

?

bar=test2(bar) # 返回的bar的內存地址,賦值給bar

bar() #run bar

?

<function bar at 0x7fe03849e620>

in the bar

?

嵌套函數?

x = 0

def grandpa():

? ? x = 1

? ? print(x)

? ? def dad():

? ? ? ? x =2

? ? ? ? print(x)

? ? ? ? def son():

? ? ? ? ? ? x =3

? ? ? ? ? ? print(x)

? ? ? ? son()

? ? dad()

?

grandpa()

?

1

2

3

?

高階函數+嵌套函數 = 裝飾器?

import time

def timer(func): #timer(test1) func=test1

? ? def deco(*args,**kwargs):? #非固定參數

? ? ? ? start_time=time.time()

? ? ? ? func(*args,**kwargs) #run test1()

? ? ? ? stop_time = time.time()

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

? ? return deco

@timer #test1=timer(test1) 把deco的內存地址返回給test1

def test1():

? ? time.sleep(1)

? ? print('in the test1')

?

@timer # test2 = timer(test2) = deco test2(name) =deco(name)

def test2(name,age):

? ? print("test2:",name,age)

?

test1() #實際上是在執行deco

test2("alex",22)

?

in the test1

the func run time is 1.001246452331543

test2: alex 22

the func run time is 0.00011372566223144531

?

類裝飾器?

沒錯,裝飾器不僅可以是函數,還可以是類,相比函數裝飾器,類裝飾器具有靈活度大、高內聚、封裝性等優點。使用類裝飾器主要依靠類的__call__方法?

?

class Foo(object):

? ? def __init__(self, func):

? ? ? ? self._func = func

?

? ? def __call__(self):

? ? ? ? print ('class decorator runing')

? ? ? ? self._func()

? ? ? ? print ('class decorator ending')

?

@Foo

def bar():

? ? print ('bar')

bar()

?

?

class decorator runing

bar

class decorator ending

?

裝飾器可以把與業務邏輯無關的代碼抽離出來,讓代碼保持干凈清爽,而且裝飾器還能被多個地方重復利用。比如一個爬蟲網頁的函數,如果該 URL 曾經被爬過就直接從緩存中獲取,否則爬下來之后加入到緩存,防止后續重復爬取。?

def web_lookup(url, saved={}):

? ? if url in saved:

? ? ? ? return saved[url]

? ? page = urllib.urlopen(url).read()

? ? saved[url] = page

? ? return page

?

pythonic?

import urllib.request as urllib # py3

def cache(func):

? ? saved= {}

? ? def wrapper(url):

? ? ? ? if url in saved:

? ? ? ? ? ? return saved [url]

? ? ? ? else:

? ? ? ? ? ? page = func(url)

? ? ? ? ? ? saved [url] = page

? ? ? ? ? ? return page

? ? return wrapper

? ??

@cache

def web_lookup(url):

? ? return urllib.urlopen(url).read()

?

用裝飾器寫代碼表面上感覺代碼量更多,但是它把緩存相關的邏輯抽離出來了,可以給更多的函數調用,這樣總的代碼量就會少很多,而且業務方法看起來簡潔了。?

帶參數的decorator?

def log(text):

? ? def decorator(func):

? ? ? ? def wrapper(*args, **kw):

? ? ? ? ? ? print('%s %s():' % (text, func.__name__))

? ? ? ? ? ? return func(*args, **kw)

? ? ? ? return wrapper

? ? return decorator

?

還差最后一步。因為我們講了函數也是對象,它有__name__等屬性,但你去看經過decorator裝飾之后的函數,它們的__name__已經從原來的’now’變成了’wrapper’:?

@log('execute')

def now():

? ? print('2015-3-25')

? ??

now()

now.__name__

?

execute now():

2015-3-25

?

?

?

?

?

'wrapper'

?

因為返回的那個wrapper()函數名字就是’wrapper’,所以,需要把原始函數的__name__等屬性復制到wrapper()函數中,否則,有些依賴函數簽名的代碼執行就會出錯。?

不需要編寫wrapper.__name__ = func.__name__這樣的代碼,Python內置的functools.wraps就是干這個事的,所以,一個完整的decorator的寫法如下:?

import functools

?

def log(text):

? ? def decorator(func):

? ? ? ? @functools.wraps(func)

? ? ? ? def wrapper(*args, **kw):

? ? ? ? ? ? print('%s %s():' % (text, func.__name__))

? ? ? ? ? ? return func(*args, **kw)

? ? ? ? return wrapper

? ? return decorator

?

?

@log('execute')

def now():

? ? print('2015-3-25')

? ??

now()

now.__name__

?

execute now():

2015-3-25

?

?

?

?

?

'now'

?

那么不帶參數decorator,也是一樣的?

import functools

?

def log(func):

? ? @functools.wraps(func)

? ? def wrapper(*args, **kw):

? ? ? ? print('call %s():' % func.__name__)

? ? ? ? return func(*args, **kw)

? ? return wrapper

?

實例—登錄認證?

import functools

?

?

user,passwd = 'sun' ,'123'

def auth(auth_type):

? ? print("auth func:",auth_type)

? ? def decorator(func):

? ? ? ? @functools.wraps(func)

? ? ? ? def wrapper(*args,**kwargs):

? ? ? ? ? ? print('wrapper func args:',*args,**kwargs)

? ? ? ? ? ? if auth_type == 'local':

? ? ? ? ? ? ? ? username = input('Username:').strip()

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

? ? ? ? ? ? ? ? if user == username and passwd == password:

? ? ? ? ? ? ? ? ? ? print("\033[32;1mUser has passed authentication\033[0m")

? ? ? ? ? ? ? ? ? ? res = func(*args, **kwargs)?

? ? ? ? ? ? ? ? ? ? print("--after authentication--")

? ? ? ? ? ? ? ? ? ? return res

? ? ? ? ? ? ? ? else:

? ? ? ? ? ? ? ? ? ? exit("\033[31;1mInvalid username or password\033[0m")

? ? ? ? ? ? elif auth_type == 'ldap':

? ? ? ? ? ? ? ? res = func(*args, **kwargs)

? ? ? ? ? ? ? ? print("搞毛線ldap,不會。。。。")

? ? ? ? ? ? ? ? return res

?

? ? ? ? return wrapper

? ? return decorator

?

def index():

? ? print("welcome to index page")

?

@auth(auth_type='local')

def home():

? ? print("welcome to home page")

? ? return 'from home'

?

@auth(auth_type='ldap')

def bbs():

? ? print("welcome to bbs page")

?

?

index()

print(home())? #wrapper

bbs()

?

?

auth func: local

auth func: ldap

welcome to index page

wrapper func args:

Username:sun

Password:123

[32;1mUser has passed authentication[0m

welcome to home page

--after authentication--

from home

wrapper func args:

welcome to bbs page

搞毛線ldap,不會。。。。

總結

以上是生活随笔為你收集整理的[转载] python迭代器、生成器和装饰器的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。