[轉載] 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,不會。。。。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/540468.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/540468.shtml
英文地址,請注明出處:http://en.pswp.cn/news/540468.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

java中的starts_Java Math類靜態double nextAfter(double starts,double direction)示例

java中的starts數學類靜態double nextAfter(雙向啟動&#xff0c;雙向) (Math Class static double nextAfter(double starts , double directions) ) This method is available in java.lang package. 此方法在java.lang包中可用。 This method is used to return the double …

Python 核心編程(第二版)——條件和循環

Python 中的 if 子句由三部分組成: 關鍵字本身&#xff0c;用于判斷結果真假的條件表達式&#xff0c; 以及當表達式為真或者非零時執行的代碼塊。if 語句的語法如下: if expression: expr_true_suite 單個 if 語句可以通過使用布爾操作符 and , or 和 not實現多重判斷條件或…

[轉載] 【python魔術方法】迭代器(__iter__和__next__)

參考鏈接&#xff1a; Python __iter __()和__next __()| 將對象轉換為迭代器 文章目錄 __iter__ 和 __next__真正的迭代器總結 python里面有很多的以__開始和結尾的函數&#xff0c;利用它們可以完成很多復雜的邏輯代碼&#xff0c;而且提高了代碼的簡潔性&#xff0c;本文主…

Silverlight 異步單元測試

Silverlight 中的很多操作都是異步的&#xff0c;很多情況下要求單元測試也是異步的&#xff0c;但是介紹異步單元測試的文檔很少。通過對 Silverlight Toolkit 中的 Microsoft.Silverlight.Testing 和 Microsoft.VisualStudio.QualityTools.UnitTesting.Silverlight 這兩個文件…

網絡拓撲 令牌環網 以太網_以太網連接中網絡拓撲的類型及其框架 以太網技術...

網絡拓撲 令牌環網 以太網A topology explains how physically the network is designed or what is the structure of the network. These designs are both physical and logical. There are many network topologies 4 like Bus, Star, Ring, and Mesh. But only two types …

Wafer晶圓封裝工藝介紹

芯片封裝的目的&#xff08;The purpose of chip packaging&#xff09;: 芯片上的IC管芯被切割以進行管芯間連接&#xff0c;通過引線鍵合連接外部引腳&#xff0c;然后進行成型&#xff0c;以保護電子封裝器件免受環境污染&#xff08;水分、溫度、污染物等&#xff09;&…

[轉載] Python中的解析式和生成器表達式

參考鏈接&#xff1a; Python | 生成器表達式 解析式和生成器表達式 列表解析List Comprehension 語法 [返回值 for 元素 in 可迭代對象 if 條件]使用中括號[],內部是for循環&#xff0c;if條件語句可選&#xff0c;會返回一個新的列表 列表解析試優點 編譯器會優化&…

java 數字字母進位_使用帶有進位的8085微處理器將兩個8位數字相乘

java 數字字母進位Problem statement: 問題陳述&#xff1a; Multiplication of two 8 bits numbers using 8085 microprocessor with carry. 使用帶有進位的8085微處理器將兩個8位數字相乘。 Algorithm: 算法&#xff1a; Load HL pair with initial data using LHLD comma…

[轉載] Python3.0中普通方法、類方法和靜態方法的比較

參考鏈接&#xff1a; Python中的類方法與靜態方法 一、語法區別 剛接觸Python中的面向對象&#xff0c;對于類方法和靜態方法難以區分&#xff0c;通過查找知乎、CSDN論壇&#xff0c;廢了好大的勁思路才逐漸明朗&#xff0c;所以就總結順便分享一下。 首先開始編輯代碼 # 普…

iOS:個人淺談工廠模式

一、什么是工廠方法&#xff1f; 正式的解釋是&#xff1a;在基類中定義創建對象的一個接口&#xff0c;讓子類決定實例化哪個類。工廠方法讓一個類的實例化延遲到子類中進行。工廠方法要解決的問題是對象的創建時機&#xff0c;它提供了一種擴展的策略&#xff0c;很好地符合了…

scanf 輸入十六進制_使用C語言中的scanf()在字符變量中輸入十進制,八進制和十六進制值...

scanf 輸入十六進制Here, we will declare an unsigned char variable and input different formats value like decimal format, octal format and hexadecimal format. 在這里&#xff0c;我們將聲明一個無符號的char變量&#xff0c;并輸入不同格式的值&#xff0c;例如十進…

[轉載] Python中pass的作用

參考鏈接&#xff1a; 如何在Python中編寫空函數&#xff1f;請使用 pass語句 空語句 do nothing保證格式完整保證語義完整 以if語句為例&#xff0c;在c或c/java中&#xff1a; if(true) ; //do nothing else { //do something } 對應于python就要這樣寫&#xff…

wrf 嵌套網格作用_在網格系統中使用響應列,嵌套列和偏移列 引導程序

wrf 嵌套網格作用介紹 (Introduction) In the previous article, we have learnt what is grid and grid system and how it works? Now, we will learn about how Responsive column, Nesting Columns and Offset Columns works and how to use them? If you have any doubt…

[看書筆記]《深入java虛擬機》——java體系結構(二)

java虛擬機的三種含義&#xff1a; - 抽象的規范 - 一個具體的實現 - 一個運行中的虛擬機實例 ---------------------java虛擬機的生命周期&#xff1a; java虛擬機實例的天職就是負責運行一個java程序。 啟動一個java程序&#xff0c;一個虛擬機實例誕生了&#xff1b;程序關閉…

[轉載] 【零基礎學爬蟲】python中的yield詳解

參考鏈接&#xff1a; 什么時候在Python中使用yield而不是return python中的yield功能比較強大&#xff0c;什么意思呢&#xff1f;如果一個函數f內使用了yield關鍵詞&#xff0c;那么該函數就可以這樣使用&#xff1a; for item in f(***): **** 也就是包含yield關鍵詞的函…

全新的membership框架Asp.net Identity(1)——.Net membership的歷史

在Asp.net上&#xff0c;微軟的membershop框架經歷了Asp.net membership到Asp.net simple membership&#xff0c;再到現在的Asp.net Identity. 每一次改變&#xff0c;都使得驗證框架更加的適應變化和可定制。這篇文章是Asp.net Identity系列的開篇&#xff0c;主要就membersh…

c語言100位整數變量聲明_C ++程序動態聲明一個整數變量并打印其內存地址

c語言100位整數變量聲明Here, we will learn how we can declare an integer variable dynamically and how to print address of declared memory block? 在這里&#xff0c;我們將學習如何動態聲明整數變量&#xff0c;以及如何打印聲明的內存塊的地址&#xff1f; In C pr…

[轉載] python 函數返回多個值

參考鏈接&#xff1a; 在Python中返回多個值 &#xff08;廖雪峰Python教程學習筆記&#xff09; 函數體內部的語句在執行時&#xff0c;一旦執行到return&#xff0c;函數就執行完畢&#xff0c;并將結果返回。 如果沒有return語句&#xff0c;函數執行完畢后也會返回結果…

二.編寫第一個c#程序(注釋,命名空間,類,Main方法,標識符,關鍵字,輸入,輸出語句,)...

復習編寫一個控制臺應用程序&#xff0c;目標是在控制臺輸出“Hello World” 1.第一步&#xff0c;打開Visual Studio 2012以上版本(我用的是VS 2015)&#xff0c;打開完成后出現以下界面 2.第二步&#xff0c;這時候就要新建一個解決方案了&#xff0c;創建解決方案可以直接點…

[轉載] Python中定義函數,循環語句,條件語句

參考鏈接&#xff1a; Python中的局部函數 由于日常程序流中主要是三種結構&#xff1a;順序&#xff0c;循環&#xff0c;條件&#xff0c;且往往需要自定義函數再調用&#xff0c; 因此今天想學習一下Python中關于定義函數、循環語句和條件語句的寫法。 1.定義函數 區…