python中的迭代器,生成器,闭包,装饰器,@property
一.迭代器
迭代器在Python中無處不在。它們在for循環,理解,生成器等中優雅地實現,但卻隱藏在眼皮底下。
Python中的Iterator只是一個可以迭代的對象。一個將返回數據的對象,一次返回一個元素。
從技術上講,Python 迭代器對象必須實現兩個特殊方法,iter()和__next__()統稱為迭代器協議。
如果我們可以從對象獲得迭代器,則該對象稱為可迭代。Python中的大多數內置容器(例如:list,tuple,string等)都是可迭代的。
iter()函數(也就是__iter__()方法)從它們返回一個迭代器。
1示例
my_list = [4, 7, 0, 3]# 使用iter()獲得迭代器 my_iter = iter(my_list) #輸出 4 print(next(my_iter)) #輸出 7 print(next(my_iter)) ## next(obj)與obj .__ next __()相同 #輸出 0 print(my_iter.__next__())2for循環
在正常編碼中,我們常常使用for循環來迭代
其真實 for 循環 內部代碼為
# 創建一個迭代器對象iterable iter_obj = iter(iterable)# 無限循環 while True:try:# 獲取下一項element = next(iter_obj)# 對元素做點什么except StopIteration:# 如果引發StopIteration,則從循環中斷break因此,在內部,for循環通過在iterable上調用iter()創建一個迭代器對象iter_obj。
具有諷刺意味的是,這個for循環實際上是一個無限的while循環。
在循環內部,它調用next()來獲取下一個元素,并使用這個值執行for循環的主體。當所有的項都用完后,StopIteration被拋出,它在內部被捕獲,循環結束。注意,任何其他類型的異常都會通過。
3構建自己的迭代器
在Python中從頭開始構建迭代器很容易。我們只需要實現這些方法__iter__()和__next__()。
iter()方法返回迭代器對象本身。如果需要,可以執行一些初始化。
next()方法必須返回序列中的下一項。在到達終點時,以及在隨后的調用中,它必須引發StopIteration。
這里,我們展示了一個示例,它將在每次迭代中為我們提供2的次冪。冪指數從0到用戶設置的數字。
class PowTwo:"""實現迭代器的類二的冪"""def __init__(self, max = 0):self.max = maxdef __iter__(self):self.n = 0return selfdef __next__(self):if self.n <= self.max:result = 2 ** self.nself.n += 1return resultelse:raise StopIterationa = PowTwo(4) print(a.__iter__()) print(a.__next__()) print(a.__next__()) print(a.__next__()) print(a.__next__()) print(a.__next__()) print(a.__next__())二.生成器
1什么是生成器
用Python構建迭代器有很多開銷; 我們必須使用__iter__()和__next__()方法實現一個類,跟蹤內部狀態,在沒有要返回的值時觸發StopIteration等等。
這既冗長又違反直覺。生成器在這種情況下可以派上用場。
Python生成器是創建迭代器的簡單方法。我們上面提到的所有開銷都由Python的生成器自動處理。
簡而言之,生成器是一個函數,它返回一個對象(迭代器),我們可以對其進行迭代(一次一個值)。
2如何創建生成器
在Python中創建生成器非常簡單。 就像使用yield語句而不是return語句定義普通函數一樣容易。
如果一個函數包含至少一個yield語句(它可能包含其他yield或return語句),那么它就成為一個生成器函數。yield和return都將從函數返回一些值。
不同之處在于,當return語句完全終止一個函數時,yield語句會暫停該函數保存其所有狀態,然后在后續調用時繼續執行。
3生成器函數和普通函數區別
生成器函數包含一個或多個yield語句。
調用時,它返回一個對象(迭代器),但不會立即開始執行。
像__iter__()和__next__()這樣的方法會自動實現。因此,我們可以使用next()來遍歷項目。
一旦函數產生了結果,函數就會暫停,控制就會轉移給調用者。
局部變量及其狀態在連續調用之間被記住。
最后,當函數終止時,在進一步調用時會自動引發StopIteration。
4代碼
# 一個簡單的生成器函數 def my_gen():n = 1print('這是第一次打印')# 生成器函數包含yield語句yield nn += 1print('這是第二次打印')yield nn += 1print('這是最后一次打印')yield na=my_gen() print(a.__next__()) print(a.__next__()) print(next(a)) print(next(a))5生成器表達式
使用生成器表達式可以輕松地動態創建簡單的生成器。它使建造生成器變得容易。
與lambda函數創建匿名函數相同,生成器表達式創建匿名生成器函數。
生成器表達式的語法類似于Python中的列表理解語法。但是將方括號替換為圓括號。
列表理解與生成器表達式之間的主要區別在于,雖然列表理解生成整個列表,但生成器表達式一次生成一個項目。
他們有點懶,只在需要時才生成項目。由于這個原因,生成器表達式比等價的列表理解的內存效率要高得多。
三.閉包
1嵌套函數中的非局部變量
在了解閉包是什么之前,我們必須首先了解什么是嵌套函數和非局部變量。
在另一個函數內部定義的函數稱為嵌套函數。嵌套函數可以訪問封閉范圍的變量。
在Python中,默認情況下,這些非本地變量是只讀的,并且我們必須將它們明確聲明為非本地變量(使用nonlocal關鍵字)才能進行修改。
以下是訪問非局部變量的嵌套函數的示例。
2定義閉包函數
在上面的示例中,如果函數print_msg()的最后一行返回了printer()函數而不是調用它,將會發生什么? 這意味著功能定義如下。
def print_msg(msg): # 這是外部封閉函數def printer(): # 這是嵌套函數print(msg)return printer # 這變了# 現在,讓我們嘗試調用此函數。 # 輸出: Hello another = print_msg("Hello") another()這很不尋常。
print_msg()函數用字符串調用,"Hello"返回的函數綁定到另一個名稱。在調用時another(),盡管我們已經完成了print_msg()函數的執行,但仍然記得該消息。
這種將一些數據(“Hello”)附加到代碼上的技術在Python中稱為閉包。
即使變量超出范圍或函數本身已從當前命名空間中刪除,也會記住封閉范圍中的這個值。
3閉包的條件
從上面的實例可以看出,在Python中,當嵌套的函數在其封閉的范圍內引用一個值時,我們有一個閉包。
以下幾點總結了在Python中創建閉包必須滿足的條件。
4何時使用閉包
那么,閉包有什么用呢?
閉包可以避免使用全局值,并提供某種形式的數據隱藏。它還可以為該問題提供面向對象的解決方案。
當在一個類中實現的方法很少(大多數情況下是一個方法)時,閉包可以提供另一種更優雅的解決方案。但是,當屬性和方法的數量變大時,最好實現一個類。
這是一個簡單的示例,其中閉包可能比定義類和創建對象更可取。
四.裝飾器
1.什么是裝飾器
裝飾器接受一個函數,添加一些功能并返回它。
Python有一個有趣的功能,稱為裝飾器,可將功能添加到現有代碼中。
這也稱為元編程,因為程序的一部分試圖在編譯時修改程序的另一部分。
2學習裝飾器的先決條件
為了了解裝飾器,我們必須首先了解Python的一些基本知識。
我們必須接受這樣一個事實,即Python中的所有內容都是對象。我們定義的名稱只是綁定到這些對象的標識符。函數也不例外,它們也是對象(帶有屬性)。可以將各種不同的名稱綁定到同一功能對象。
這是一個實例。
當您運行代碼時,這兩個函數first和second給出相同的輸出。在此,名稱first和second指代相同的功能對象。
現在情況是不是感覺變復雜了點,可以將函數作為參數傳遞給另一個函數。
如果您在Python中使用過map,filter和reduce之類的函數,那么您已經知道這一點。
這種以其他函數為參數的函數也稱為高階函數。這是這種函數的一個實例。
此外,一個函數可以返回另一個函數。 即閉包
def is_called():def is_returned():print("Hello")return is_returnednew = is_called()#輸出 "Hello" new()3回到裝飾器
函數和方法被稱為可調用的,因為它們可以被調用。
實際上,任何實現特殊方法__call __()的對象都稱為可調用的。 因此,從最基本的意義上講,裝飾器是可調用的,可返回可調用的。
基本上,裝飾器接受一個函數,添加一些功能并返回它。
在上面顯示的示例中,make_pretty()是一個裝飾器。在分配步驟中。
pretty = make_pretty(ordinary)
函數ordinary()被修飾,返回的函數被命名為pretty。
我們可以看到decorator函數在原來的函數中添加了一些新功能。這類似于包裝禮物。裝飾器充當包裝器。被裝飾的物品(里面的禮物)的性質不會改變。但是現在,它看起來很漂亮(自從裝飾之后)。
通常,我們裝飾一個函數并將其重新分配為
ordinary = make_pretty(ordinary).
這是一個常見的構造,因此,Python具有簡化此語法的語法。
我們可以將@符號與裝飾器函數的名稱一起使用,并將其放置在要裝飾的函數的定義上方。例如,
相當于
def ordinary():print("我是普通的函數") ordinary = make_pretty(ordinary)4.帶參數的裝飾器
裝飾器里有很多閉包知識
通過這種方式,我們可以裝飾帶有參數的函數。
敏銳的觀察者會注意到,inner()裝飾器內部的嵌套函數的參數與其裝飾的函數的參數相同。考慮到這一點,現在我們可以使通用裝飾器可以使用任意數量的參數。
在Python中,此魔術是通過完成的function(*args, **kwargs)。這樣,args是位置參數的元組,kwargs而是關鍵字參數的字典。這樣的裝飾器的一個實例是。
5.鏈接裝飾器
可以在Python中鏈接多個裝飾器。
這就是說,一個函數可以用不同(或相同)的裝飾器多次裝飾。我們只需將裝飾器放置在所需函數之上。
五.@property
在定義和詳細了解@property是什么之前,讓我們了解為什么首先需要使用它。、
一個實例開始
假設您決定創建一個以攝氏度為單位存儲溫度的類。它還將實現一種將溫度轉換為華氏溫度的方法。其中一種方法如下。
如上所示,每當我們分配或檢索任何對象屬性(如temperature)時,Python都會在對象的__dict__字典中進行搜索。
man.dict
{‘temperature’: 37}
現在,讓我們進一步假設我們的課程在客戶中很受歡迎,并且他們開始在程序中使用它。 他們對對象進行了各種分配。
有一天,一個值得信賴的客戶來找我們,建議溫度不能低于-273攝氏度(熱力學專業的學生可能會說實際上是-273.15攝氏度),也被稱為絕對零度。他進一步要求我們實現這個值約束。作為一家追求客戶滿意度的公司,我們很高興地聽取了這個建議,并發布了1.01版本(對現有類的升級)。
1 使用getter和setter
解決上述約束的一個明顯方法是隱藏屬性temperature(將其設為私有),并定義新的getter和setter接口以對其進行操作。這可以如下進行。
我們在上面可以看到get_temperature(),set_temperature()已經定義了新方法,此外,用_temperature替換了temperature。下劃線(_)開頭表示Python中的私有變量。
c = Celsius(-277)
Traceback (most recent call last):
ValueError: Temperature below -273 is not possible
c = Celsius(37)
c.get_temperature()
37
c.set_temperature(10)
c.set_temperature(-300)
Traceback (most recent call last):
ValueError: Temperature below -273 is not possible
但這不是一個大問題。上述更新的最大問題在于,所有在程序中實現了上一類的客戶端都必須將其代碼從obj.temperature修改為obj.get_temperature(),并將所有分配(例如obj.temperature = val修改為obj.set_temperature( val))。
這種重構會給客戶帶來數十萬行代碼的麻煩。
總而言之,我們的新更新不向后兼容。這是@property發揮作用的地方。
2@property的力量
python處理上述問題的方法是使用property。我們可以這樣來實現它。
同樣,任何訪問如c.temperature都會自動調用get_temperature()。 這就是屬性的作用。 這里還有一些實例。
同樣,任何設定.temperature都會自動調用set_temperature()。 這就是屬性的作用。
任何檢索溫度值的代碼都將自動調用get_temperature()而不是字典(dict)查找。 同樣,任何為溫度分配值的代碼都會自動調用set_temperature()。 這是Python中的一項很酷的功能。
我們可以在上面看到即使創建對象時也會調用set_temperature()。
3深入了解property
在Python中,property()是一個內置函數,用于創建并返回屬性對象。該函數的簽名是
property(fget=None, fset=None, fdel=None, doc=None)
其中,fget為獲取屬性值的函數,fset為設置屬性值的函數,fdel為刪除屬性的函數,doc為字符串(如注釋)。從實現中可以看出,這些函數參數是可選的。因此,可以簡單地按照以下方式創建屬性對象。
屬性對象有三個方法,getter()、setter()和deleter(),用于稍后指定fget、fset和fdel。這意味著
temperature = property(get_temperature,set_temperature)
也可以分解為
創建空屬性
temperature = property()
設置 fget
temperature = temperature.getter(get_temperature)
#設置 fset
temperature = temperature.setter(set_temperature)
熟悉Python中裝飾器的程序員可以認識到上述構造可以實現為裝飾器。
我們可以更進一步,不定義名稱get_temperature,set_temperature,因為它們是不必要的,并且會影響類命名空間。為此,我們在定義getter和setter函數時重用了名稱temperature。這是可以做到的。
class Celsius:def __init__(self, temperature = 0):self._temperature = temperaturedef to_fahrenheit(self):return (self.temperature * 1.8) + 32@propertydef temperature(self):print("獲得值")return self._temperature@temperature.setterdef temperature(self, value):if value < -273:raise ValueError("零下273度是不可能的")print("設定值")self._temperature = value
電氣工程的計算機萌新:余登武。
寫博文不容易,如果你覺得本文對你有用,請點個贊支持下,謝謝。
總結
以上是生活随笔為你收集整理的python中的迭代器,生成器,闭包,装饰器,@property的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 家用小电器大全集(家用小电器大全)
- 下一篇: MATLAB-1:入门基础