裝飾器(本質:函數)
定義:為其他函數添加附加功能(裝飾器對于被裝飾函數是“透明的”、“不存在的”);執行過程:調用被裝飾函數實際上是調用修飾函數
原則:
不能修改被裝飾函數的源代碼;
不能修改被裝飾函數的調用方式。
實作裝飾器的知識補充:
函數即“變量”:
調用前先定義(即要先有記憶體位址空間、函數的執行順序)
可指派給其他函數名

<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 < 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))
<generator object <genexpr> at 0x054D1F30>
方法二:在函數定義中包含yield關鍵字。
這時,這個函數就不再是一個普通函數,首次調用__next__()的時候執行生成器函數,遇到yield語句時傳回,再次執行(__next__()或send()或for、while等)時将從上次傳回的yield語句處繼續執行。
# print(b)
yield b
return "done"
fib(5)
print(fib(5)) #此時不會擷取return的值
輸出:
<generator object fib at 0x04F8AF90>
擷取生成器的值:
使用__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()
>>> from collections import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
>>> isinstance('abc', Iterable)
>>> isinstance((x for x in range(10)), Iterable)
>>> isinstance(100, Iterable)
False
判斷一個對象是否是Iterator對象:
>>> from collections import Iterator
>>> isinstance((x for x in range(10)), Iterator)
>>> isinstance([], Iterator)
>>> isinstance({}, Iterator)
>>> isinstance('abc', Iterator)
把Iterable對象變成Iterator對象:
#使用iter()函數:
>>> isinstance(iter([]), Iterator)
>>> isinstance(iter('abc'), Iterator)
#将清單變為iterator對象
a = [1,2,3,4]
b=iter(a)
print(type(b))
print(b.__next__())
#輸出
<class 'list_iterator'>
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