python三大法器:生成器、裝飾器、迭代器

迭代器

迭代的概念

使用for循環遍歷取值的過程叫做迭代,比如:使用for循環遍歷列表獲取值的過程

使用for循環遍歷取值的對象叫做可迭代對象, 比如:列表、元組、字典、集合、range、字符串

判斷對象是否是指定類型

~~~

from collections import Iterable

result = isinstance((3,5), Iterable)

~~~

自定義可迭代對象: 在類里面定義__iter__方法創建的對象就是可迭代對象

可迭代對象的本質:遍歷可迭代對象的時候其實獲取的是可迭代對象的迭代器, 然后通過迭代器獲取對象中的數據

自定義迭代器對象: 在類里面定義__iter__和__next__方法創建的對象就是迭代器對象

注:在這里有兩個概念,一個是?可迭代對象 一個是?迭代器對象 不要混淆

demo:

~~~

from collections import Iterator

# 自定義可迭代對象: 在類里面定義__iter__方法創建的對象就是可迭代對象

class MyList(object):

????def __init__(self):

????????self.my_list =list()

????# 添加指定元素

? ? def append_item(self, item):

????????self.my_list.append(item)

????def __iter__(self):

????# 可迭代對象的本質:遍歷可迭代對象的時候其實獲取的是可迭代對象的迭代器, 然后通過迭代器獲取對象中的數據

? ? ? ? my_iterator = MyIterator(self.my_list)

????????return my_iterator

# 自定義迭代器對象: 在類里面定義__iter__和__next__方法創建的對象就是迭代器對象

class MyIterator(object):

????def __init__(self, my_list):

????????self.my_list = my_list

????????# 記錄當前獲取數據的下標

? ? ? ? self.current_index =0

? ? ? ? # 判斷當前對象是否是迭代器

? ? ? ? result =isinstance(self, Iterator)

????????print("MyIterator創建的對象是否是迭代器:", result)

????def __iter__(self):

????????return self

? ? # 獲取迭代器中下一個值

? ? def __next__(self):

????????if self.current_index

????????????self.current_index +=1

? ? ? ? ????return self.my_list[self.current_index -1]

????????else:

????????????# 數據取完了,需要拋出一個停止迭代的異常

? ? ? ? ? ? raise StopIteration

my_list = MyList()

my_list.append_item(1)

my_list.append_item(2)

result =isinstance(my_list, Iterable)

print(result)

for value in my_list:

????print(value)

~~~

iter()函數與next()函數

iter函數: 獲取可迭代對象的迭代器,會調用可迭代對象身上的__iter__方法

next函數: 獲取迭代器中下一個值,會調用迭代器對象身上的__next__方法

?for循環的本質

遍歷的是可迭代對象

for item in Iterable 循環的本質就是先通過iter()函數獲取可迭代對象Iterable的迭代器,然后對獲取到的迭代器不斷調用next()方法來獲取下一個值并將其賦值給item,當遇到StopIteration的異常后循環結束。

遍歷的是迭代器

for item in Iterator 循環的迭代器,不斷調用next()方法來獲取下一個值并將其賦值給item,當遇到StopIteration的異常后循環結束。

生成器

生成器是一類特殊的迭代器,它不需要再像上面的類一樣寫__iter__()和__next__()方法了, 使用更加方便,它依然可以使用next函數和for循環取值

創建生成器方法1

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

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

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

(i for i in range(5))

如果我們打印它的類型我們可以看到是<class 'generator'>?

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

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

如果要一個一個打印出來,可以通過generator的next()方法:

創建生成器方法2

在def函數里面看到有yield關鍵字那么就是生成器

demo(斐波那契額數列)

~~~

def fibonacci(num):

????a =0

? ? b =1

? ? # 記錄生成fibonacci數字的下標

? ? current_index =0

????while current_index < num:

????????result = a

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

????????current_index +=1

????????# 代碼執行到yield會暫停,然后把結果返回出去,下次啟動生成器會在暫停的位置繼續往下執行

? ? ? ? yield result

for i in?fibonacci(5):

? ? print(i)

只要在def中有yield關鍵字的 就稱為 生成器

使用send方法啟動生成器并傳參

def gen():

? ?i =0

? ?while i<5:

? ? ? ? temp =yield i????

????????print(temp)

????????i+=1

我們可以調用send方法給yield傳值

f = gen()

next(f)? ?#首次調用必須是next,因為生成器還沒有執行到yield 無法進行接收值

f.send('haha')? ?#send 發送值 傳遞給生成器 由temp進行接收

next(f)? ?#再次調用next? 相當于send了一個None

f.send('haha')

~~~

yield和return的對比

使用了yield關鍵字的函數不再是函數,而是生成器。(使用了yield的函數就是生成器)

代碼執行到yield會暫停,然后把結果返回出去,下次啟動生成器會在暫停的位置繼續往下執行

每次啟動生成器都會返回一個值,多次啟動可以返回多個值,也就是yield可以返回多個值

return只能返回一次值,代碼執行到return語句就停止迭代,拋出停止迭代異常

yield可以在下次執行的時候接受傳遞的值


生成器創建有兩種方式,一般都使用yield關鍵字方法創建生成器

yield特點是代碼執行到yield會暫停,把結果返回出去,再次啟動生成器在暫停的位置繼續往下執行


裝飾器

裝飾器使用的原理就是閉包,在函數的時候介紹過閉包的一個使用:

http://www.lxweimin.com/p/40ab45debf43

裝飾器本質上是一個Python函數,它可以讓其他函數在不需要做任何代碼變動的前提下增加額外功能,裝飾器的返回值也是一個函數對象。它經常用于有切面需求的場景,比如:插入日志、性能測試、事務處理、緩存、權限校驗等場景。裝飾器是解決這類問題的絕佳設計,有了裝飾器,我們就可以抽離出大量與函數功能本身無關的雷同代碼并繼續重用。

概括的講,裝飾器的作用就是為已經存在的函數或對象添加額外的功能

簡單裝飾器

~~~

def test(fun):

????def test_in():

????#增加的功能

? ? ? ? print("這是我新增的功能")

????????return? fun()? ? #一般我們把函數的結果返回,不會自己直接執行

????# 其實這里返回的就是閉包的結果

? ? return test_in#返回一定是內部函數的引用,不能帶有小括號

@test

def fun():

????return "rookieyu"

print(fun())

~~~

簡單的裝飾器,函數沒有參數傳遞

裝飾器 @test

相當于 fun = test(fun)??

在這里調用的test ,然后把fun這個函數當做一個對象傳遞給test函數,返回的是test_in函數重新賦值給fun

最后我們在執行fun()的時候,相當于執行的是test(fun)() 是test_in函數。執行后會在函數里面先執行我們增加的功能,之后把函數之后的結果返回。

函數帶參數裝飾器

~~~

def debug(func):

????def wrapper(*args, **kwargs):# 指定宇宙無敵參數

? ? ? ? print("[DEBUG]: enter {}()".format(func.__name__))

????????print('Prepare and say...',)

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

????return wrapper? # 返回


@debug

def say(something):

????print("hello {}!".format(something))

say("rookieyu")

~~~

我們采用的是*args, **kwargs,這樣我們再調用裝飾器的時候可以隨意設置變量,讓我們的裝飾器更具有通用性


裝飾器帶參數,在原有裝飾器的基礎上,設置外部變量

~~~

def logging(level):

????def wrapper(func):

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

????????????print("[{level}]: enter function {func}()".format(level=level,func=func.__name__))

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

????????return inner_wrapper

????return wrapper


@logging(level='INFO')

def say(something):

????print("say {}!".format(something))

# 如果沒有使用@語法,等同于

# say = logging(level='INFO')(say)

@logging(level='DEBUG')

def do(something):

????print("do {}...".format(something))

if __name__ =='__main__':

????say('hello')

????do("my work")

~~~

在這里我們可以看到裝飾器也可以傳參數的,但是我們必須要再去做一層嵌套的使用。


基于類實現的裝飾器

裝飾器函數其實是這樣一個接口約束,它必須接受一個callable對象作為參數,然后返回一個callable對象。在Python中一般callable對象都是函數,但也有例外。只要某個對象重載了__call__()方法,那么這個對象就是callable的。

demo:

~~~

class logging(object):

????def __init__(self, func):

????????self.func = func

????def __call__(self, *args, **kwargs):

????????print("[DEBUG]: enter function {func}()".format(func=self.func.__name__))

????????return self.func(*args, **kwargs)

@logging

def say(something):

????print("say {}!".format(something))

say("rookie")

~~~

內置的裝飾器

內置的裝飾器和普通的裝飾器原理是一樣的,只不過返回的不是函數,而是類對象,所以更難理解一些。

@property? 可以將一個方法,當做一個屬性來使用

@staticmethod? ?將類方法裝換成一個靜態方法

@classmethod? 將類方法裝換成為類方法

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,786評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,656評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,697評論 0 379
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,098評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,855評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,254評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,322評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,473評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,014評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,833評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,016評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,568評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,273評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,680評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,946評論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,730評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,006評論 2 374

推薦閱讀更多精彩內容