文章目錄
- 迭代器
- 概念
- 生成器
- 概念
- yield語法
- 用途
- 修飾器
- 修飾器模式
- Python修飾器
- 定義
- 應用

yield 英 [ji?ld] 美 [ji?ld]
v.出產(作物);產生(收益、效益等);提供;屈服;讓步;放棄;繳出
n.產量;產出;利潤
上面路牌是「讓」的意思

迭代器
概念
迭代器是什么?學習過C/C++的朋友可能熟悉,是在STL中用來對特定的容器進行訪問,能夠遍歷STL容器中的元素,
迭代器提供一些基本運算子:*、++、==|!+、=等,這些操作和C/C++操作array元素時的指標介面基本一致,不同的是迭代器具有遍歷復雜資料結構的能力,即所謂的智能指標,
比如對串列和元組做for...in遍歷操作時,Python實際上時通過串列和元組的迭代物件來實作的,而不是串列和元組本身:

Python中,迭代器還擁有迭代用戶自定義類的能力,迭代器物件需要支持__iter__()和next()兩個方法,前者回傳迭代器本身,后者回傳下一個元素,
迭代自定義類舉例:
class example(object):
def __init__(self,num):
self.num=num
def __iter__(self):
return self
def __next__(self):
if self.num <= 0:
raise StopIteration
tmp = self.num
self.num -= 1
return tmp
a = example(3)
print(a.__next__())
print(a.__next__())
print(a.__next__())
print(a.__next__())
類example是一個迭代物件,每次執行next()操作時會判斷self.num屬性,如果<=0則拋出例外表示迭代結束,

生成器
概念
當類線性遍歷操作時,可以通過迭代器的__iter()__和__next__()方法來實作,但是不夠靈活方便,比如對函式來說沒有屬性變數來存放狀態,即不支持函式的線性遍歷,
那這怎么解決?Python3.X支持使用yield生成器的方法來進行線性遍歷,yield陳述句僅用于定義生成器函式,且只能出現在生成器函式內,當生成器函式被呼叫時回傳一個生成器,
那生成器又是什么?生成器的概念源于協同作業的程式,比如消費者和生產者模型,Python生成器就是其中生產者的角色(資料提供者),每次生成器程式就等在那里,一旦消費者/用戶呼叫next()方法,生成就繼續執行下一步,然后把當前遇到的內部資料的Node放到下一個消費者用戶能夠看到的公用緩沖區里,然后停下來等待wait,最后消費者/用戶從緩沖區里獲得Node,
例如實作一個遞增的生成器:
def add():
num = 0
while True:
yield num
num += 1
a = add()
print(next(a))
print(next(a))
print(next(a))
定義生成器函式add(),只有在用戶呼叫next()方法時,才將內部資料的Node提供出來然后等待,而不是陷入無限回圈,

從上看出yield運算式的功能可以分成兩點:
- 回傳資料num
- 進入wait_and_get狀態,可以理解為程式在這個位置暫停,當消費者再次呼叫`next()方法時,程式才會這個位置激活,
迭代器VS生成器:
都是用戶通過next()方法來獲取資料,不過迭代器是通過自己實作的next()方法來逐步回傳資料,而生成器是使用yield自動提供資料并讓程式暫停wait狀態,等待用戶進一步操作,即生成器更加靈活方便,
yield語法
一、yield是運算式
在Python3.X中,yield成為運算式,不再是陳述句,但是必須放在函式內部,如果寫成陳述句的形式會報錯(實際上回傳值被扔掉了),例如:
yield n
x=yield n
既然yield是運算式,所以可以和其他運算式組合使用,例如:
x=y+z*(yield 2)
a=b+c+yield d
二、next()方法
當用戶呼叫next()方法執行到yield運算式時,先回傳n,然后程式進入wait狀態,只有當下一次執行next()方法時才會從此處恢復并繼續執行下面的代碼,一直執行到下一個yield運算式,如果沒有下一個yield代碼,就拋出StopIteration例外,

三、send(msg)方法
執行一個send(msg)會恢復生成器的運行,然后發送的值msg將成為當前yield運算式的回傳值,程式恢復運行之后,會繼續執行下面的代碼,也是一直執行到下一個yield代碼,如果沒有下一個則拋出StopIteration例外,
當使用send(msg)發送訊息給生成器時,wait_and_get會檢測到這個新型,然后喚醒生成器,同時該方法獲取msg并復制給x,如下代碼所示:

上述函式等價于:
def test():
print('記得一鍵三連')
put(1)
x = wait_and_get()
print('x=', x)
put(2)
y = wait_and_get()
print('y=', y)
print('下面無yield了,會拋Stop例外')
當第一次呼叫next()方法執行到第一個wait_and_get處時生成器進入wait狀態并列印‘記得一鍵三連’和’1’;
接著呼叫send(666),從第一個wait_and_get處啟動生成器,并把引數666賦值給變數x,然后繼續執行到第二個wait_and_get處,生成器又進入wait狀態;
接著呼叫send(888),生成器從第二個wait_and_get處啟動,并把引數888賦值給變數y,后面因為沒有yield運算式了,所以生成器拋出StopIteration例外,
特別注意的是,第一個呼叫要么使用next(),要么使用send(None),不能使用send()來發送一個非None值,原因是非None值是發給wait_and_get的,一開始程式并沒有停在wait_and_get代碼處,只有先使用next()或者send(None)方法后才會停止wait_and_get處,這時才能使用send發送一個非None值,


四、throw()方法
生成器提供throw()方法從生成器內部來引發例外,從而控制生成器的執行,

GeneratorExit的作用是讓生成器有機會執行一些退出時的清理作業,
五、關閉生成器
生成器提供了一個close()方法來關閉生成器,當使用close()方法時,生成器會直接從當前狀態退出,再使用next()時會得到StopIteration例外,

實際上,close()方法也是通過throw()方法來發送GenerationExit例外來關閉生成器的,其實作相當于如下代碼:
def close(self):
try:
self.throw(GeneratorExit)
except(GeneratorExit,StopIteration):
pass
else:
raise RuntimeError("generator ignored GeneratorExit")
(
插播反爬資訊)博主CSDN地址:https://wzlodq.blog.csdn.net/
用途
一、節省記憶體
相對于串列、元組,生成器更節省記憶體,生成器一次產生一個資料項,直到沒有為止,在for回圈中可以對它進行回圈處理,占用記憶體更少,但是需要記住當前的狀態,以便回傳下一個資料項,生成器是一個有next()方法的物件,序列型別則保存了所有的資料項,通過索引來進行訪問,
比如求閏年:


二、線性遍歷
生成器可以將非線性話的處理轉換為線性的方式,典型的例子就是對二叉樹的訪問,
傳統做法是用遞回:
def deal_tree(node):
if not node:
return
if node.leftnode:
deal_tree(node.leftnode)
process(node)
if node.rightnode:
deal_tree(node.rightnode)
遞回做法中為了處理每一個節點,需要將處理方法process()放到訪問的程序中,極容易出錯,也不清晰,比較好的方法是先將樹的節點訪問轉換成線性,用生成器實作:
def deal_tree(node):
if not node:
return
if node.leftnode:
for i in walk_tree(node.leftnode):
yield i
yield node
if node.rightnode:
for i in walk_tree(node.rightnode):
yield i
修飾器
修飾器模式
修飾器是用于擴展原來函式功能的一種函式,這個函式的特殊之處在于回傳值是一個函式,
修飾器(Decorator)的概念來自于設計模式,也叫裝飾者模式,具體細節可見設計模式系列博客,舉個例子,如游戲英雄的戰力提升,可以通過升級裝備、加技能、使用道具等等,實作時自然不可能把每一種組合的情況都創建出來以備呼叫,修飾器則發揮作用了:升級裝備時使用升級裝備的修飾器;使用道具時用道具的修飾器,目的是為了運行時動態的改變物件的狀態而不是編譯期,使用組合的方式來增減Decorator而不是修改原有的代碼來滿足業務的需要,以利于程式的擴展,
修飾器模式是針對Java語言的,為了靈活使用組合的方式來增減Decorator,Java語言需要使用較為復雜的類物件結構才能達到效果,Python從語法層次上實作了使用組合的方式來增減Decorator的功能,
Python修飾器
Python從語法層次上支持Decorator模式的靈活呼叫,主要有以下兩種方式:
一、不帶引數的Decorator
語法形式如下:
@A
def f():
相當于在函式f上加一個修飾器A,Python會處理為f = A(f),
修飾器的添加時不受限制的,可以多層次使用:
@A
@B
@C
def f():
Python會處理為f=A(B(C(f))),
二、帶引數的Decorator
語法形式如下:
@A(args)
def f():
Python會先執行A(args)得到一個decorator()函式,處理為:
def f():...
_deco = A(args)
f = _deco(f)
定義
每一個Decorator都對應有相應的函式,要對后面的函式進行處理,要么回傳原來的函式物件,要么回傳一個新的函式物件,特別注意Decorator只能用來處理函式和類方法,
根據上述修飾器兩種呼叫方法,修飾器函式定義也對應兩種方法:
一、不帶引數
對func處理后回傳原函式物件,語法形式如下:
def A(func):
#處理func
return func
@A
def f(args):pass
回傳一個新的函式物件,注意neew_func的定義形式要與待處理函式相同(可以用不定引數),語法形式如下:
def A(func):
def new_func(*args,**argkw):
#做一些額外作業
return func(*args,**argkw) #呼叫原函式繼續進行處理
return new_func
@A
def f(args):pass
上述代碼在A中定義了新的函式,然后A回傳這個新函式,在新函式中可以先處理一些事情再呼叫原始函式進行處理,如果想在呼叫函式之后再進一步處理,可以通過函式回傳值來實作:
def A(args):
def new_func(*args,**argkw):
#一些呼叫前處理作業
result = func(*args,**argkw)
if result:
#進一步呼叫后作業
return new_result
else:
return result
return new_func
@A
def f(args):pass
二、帶引數
因為decorator()函式使用了引數進行呼叫,所以要回傳一個新的decorator()函式,這樣就與第一種形式一致了,
def A(arg):
def _A(func):
def new_func(args):
#做一些作業
return func(args)
return new_func
return _A
@A(arg)
def f(args):pass
應用
使用Decorator可以增加程式的靈活性,減少耦合度,適合用于用戶登錄檢查、日志處理等,這種通過函式之間相互結合的方式更符合搭積木的要求,可以把函式功能進一步分解,使得功能足夠簡單單一,然后再通過Decorator的機制靈活把函式串起來,
應用舉例:一個多用戶使用的程式會有很多功能和權限相關,傳統方法是建立權限角色類,然后每個用戶繼承權限角色,但這種方法不但容易出錯,而且對管理、修改也很麻煩,采用Decorator來處理,通過decorator()函式的呼叫來處理用戶權限的邏輯,可以先定義權限管理的類:
class Permission:
#普通用戶
def userPermission(self,function):
def new_func(*args,**kwargs):
obj = args[0]
if obj.user.hasUserPermission(): #判斷擁有對應權限
ret = function(*args,**kwargs)
else:
ret = 'No User Permission'
return ret
return new_func
#管理員
def adminPermission(self,function):
def new_func(*args,**kwargs):
obj = args[0]
if obj.user.hasAdminpermission(): #判斷擁有對應權限
ret = function(*args,**kwargs)
else:
ret ='No Admin Permission'
return ret
return new_func
然后處理實際業務代碼是,只要為需要的功能加上實際權限限制Decorator就可以了:
class Action:
def __init__(self,name):
self.user = UserService.newUser(name)
@Permission.userPermission #需要用戶權限
def listAllpoints(self):
return 'do real list all points'
@Permission.adminPermission #需要管理員權限
def setup(self):
return 'do real setup'
最后就是業務方法的呼叫了:
if __name__ == "main":
action = Action('user')
print(action.listAllpoints())#執行真正的業務代碼
print(action.setup)
相對于傳統方法,Decorator使用起來優勢很大,可以將用戶權限檢查這些瑣碎的作業和業務呼叫代碼相剝離,并且能夠檢測函式方便地修飾到業務邏輯代碼之上,
Python系列博客持續更新中
原創不易,請勿轉載(
本不富裕的訪問量雪上加霜)
博主首頁:https://wzlodq.blog.csdn.net/
微信公眾號:唔仄lo咚鏘
如果文章對你有幫助,記得一鍵三連?
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/263457.html
標籤:python
