內容簡述:
- 1、迭代器
- 2、生成器
- 3、裝飾器
1、迭代器
迭代器是Python提供的用於訪問集合
,是一種可以記住遍歷位置的物件
,會從第一個元素開始訪問,直到結束。可以通過內建的iter()函式
來獲
對應的迭代器物件
,然後直接迴圈遍歷這個迭代器物件;或者通過另外一個內建的next()函式
,返回容器的下一個元素
,不過如果超過結尾會報StopIteration異常
,使用程式碼示例如下:
import sys
a = [1, 2, 3, 4, 5]
it1 = iter(a)
# 直接遍歷迭代器物件
for x in it1:
print(x, end='\t')
else:
print()
# 每呼叫一次next向後訪問一個元素,超過元素會報StopIteration異常
it2 = iter(a)
print(next(it2), end='\t')
print(next(it2), end='\t')
print(next(it2), end='\t')
print(next(it2), end='\t')
print(next(it2), end='\t')
print()
# 帶異常捕獲方法
it3 = iter(a)
while True:
try:
print(next(it3), end='\t')
except StopIteration:
sys.exit()
複製程式碼
執行結果如下:
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
複製程式碼
另外有一點要進行區分:iterable
和 iterator
,前者只實現了iter函式
,而後者同時實現了iter和next函式
。比如List只是一個iterable而不是iterator,所以只支援iter(),不支援next(),需要呼叫iter()函式獲得一個iterator物件。如果呼叫下type(it1),會發現資料型別是:<class 'list_iterator'>
,這是Python為list提供的預設迭代器物件。我們也可以自己來實現一個iterator,程式碼示例如下:
class MyIterator():
def __init__(self, names):
self.names = names
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index >= len(self.names):
raise StopIteration("已到達末尾")
else:
self.index += 1
return self.names[self.index - 1]
iterator = MyIterator([1, 2, 3, 4, 5])
for i in iterator:
print(i, end='\t')
複製程式碼
執行結果如下:
1 2 3 4 5
複製程式碼
2、生成器
叫「生成器函式」會更貼切一些,一種特別的函式,用yield關鍵字
來返回一個生成器物件,本質上還是迭代器,只是更加簡潔,yield對應的值在函式呼叫時候不會立即返回,只有去呼叫next()函式
的時候才會返回,而使用for xxx in xxx的時候其實呼叫的還是next()函式
,最簡單的生成器程式碼示例如下:
def func(n):
yield n * n
if __name__ == '__main__':
print(func(10))
print(next(func(10)))
for i in func(10):
print(i)
複製程式碼
執行結果如下:
<generator object func at 0x0000015D06436308>
100
100
複製程式碼
可以看到返回的物件型別是generator
,相比起迭代器,生成器顯得更加優雅簡潔,比如最經典的實現斐波那契數列的例子:
def func(n):
a, b = 0, 1
while n > 0:
n -= 1
yield b
a, b = b, a + b
for i in func(10):
print(i, end="\t")
複製程式碼
執行結果如下:
1 1 2 3 5 8 13 21 34 55
複製程式碼
3、裝飾器
本質上也是「函式」,作用是:幫助其他函式在不改動程式碼的情況下增加額外的功能,裝飾器函式的返回值也是一個函式。
① 一步步瞭解裝飾器
裝飾器對於很多初學者來說有點難以理解,舉個簡單的奶茶例子慢慢引入吧(因為手邊剛好有一杯一點點奶茶,另外函式名不要用中文,這裡用中文只是想讓讀者更容易理解,還有命名習慣:類名駝峰命名,函式名下劃線分割命名!)
import time
def 波霸奶茶():
time.sleep(1)
print("製作一杯波霸奶茶")
if __name__ == '__main__':
波霸奶茶()
複製程式碼
執行結果如下:
製作一杯波霸奶茶
複製程式碼
現在筆者想知道製作一杯波霸奶茶花費的時間,可以改動下波霸奶茶這個函式:
def 波霸奶茶():
start = time.clock()
time.sleep(2)
print("製作一杯波霸奶茶")
end = time.clock()
print("耗時", end - start)
if __name__ == '__main__':
波霸奶茶()
複製程式碼
執行結果如下:
製作一杯波霸奶茶
耗時 1.004335880279541
複製程式碼
這個時候問題來了,除了波霸奶茶還有四季奶綠這款奶茶:
def 四季奶綠():
time.sleep(2)
print("製作一杯四季奶綠")
複製程式碼
而我也想知道四季奶綠的製作時間,需要把統計時間那部分程式碼貼上複製一下,有點繁瑣,如果還有一款格雷三兄弟,也是要複製下,好繁瑣啊。有沒有什麼方法能簡化這個過程呢?答案肯定是有的:「Python支援函式作為引數
」,我們可以把計時部分剝離成一個單獨的函式,然後把需要檢視製作時間的奶茶函式傳入即可,所以有了下面這樣的程式碼:
def 波霸奶茶():
time.sleep(1)
print("製作一杯波霸奶茶")
def 四季奶綠():
time.sleep(2)
print("製作一杯四季奶綠")
def 計時(func):
start = time.time()
func()
end = time.time()
print("耗時", end - start)
if __name__ == '__main__':
計時(波霸奶茶)
計時(四季奶綠)
複製程式碼
執行結果如下:
製作一杯波霸奶茶
耗時 1.0002198219299316
製作一杯四季奶綠
耗時 2.0016701221466064
複製程式碼
可以,這個時候問題又來了,上面的程式碼意味著:所有呼叫到製作奶茶方法的地方都要改成計時(奶茶),如果奶茶有N多種的話,每個呼叫製作奶茶的函式都要改成這樣的形式,有沒有更簡單的方法啊?答案肯定是有的,Python支援返回一個函式。我們要做的是通過一個內嵌的包裝函式,為傳入的函式附加功能,然後返回這個函式,具體的程式碼如下:
def 計時(func):
def decorator():
start = time.time()
func()
end = time.time()
print("耗時", end - start)
return decorator
if __name__ == '__main__':
波霸奶茶 = 計時(波霸奶茶)
波霸奶茶()
四季奶綠 = 計時(四季奶綠)
四季奶綠()
複製程式碼
執行結果如下:
製作一杯波霸奶茶
耗時 1.0033059120178223
製作一杯四季奶綠
耗時 2.0044848918914795
複製程式碼
可以,沒毛病,而且只有在我們需要查詢某種奶茶的製作時間時才需要這樣寫,平常的函式還是正常使用。嗯,你以為到這裡就完了,對於裝飾器函式,Python還為我們提供了一枚語法糖,語法糖可以理解成一種便捷的寫法吧,在Python中通過一個@就可以簡化我們上面的程式碼,具體程式碼如下:
def 計時(func):
def decorator():
start = time.time()
func()
end = time.time()
print("耗時", end - start)
return decorator
@計時
def 波霸奶茶():
time.sleep(1)
print("製作一杯波霸奶茶")
@計時
def 四季奶綠():
time.sleep(2)
print("製作一杯四季奶綠")
if __name__ == '__main__':
波霸奶茶()
四季奶綠()
複製程式碼
一樣的執行結果,更加精簡的程式碼,相信讀者都體驗到了語法糖的便利,但是要注意一點:大部分的語法糖只是減少我們寫繁瑣程式碼的時間,讓我們把更多的時間花在邏輯流程上,程式碼寫少了不一定就會帶來效能上的提升,底層的程式碼可能還是那些東西!
② 帶引數的裝飾器
上面演示的是無引數的裝飾器的用法,有時我們可能需要傳入一些引數,需要在原閉包的基礎上再加一層閉包
,具體流程與程式碼示例如下:
import time
def 計時(配料="珍珠"):
def decorator(func):
def inner():
print("加入配料:%s" % 配料 )
start = time.time()
func()
end = time.time()
print("耗時", end - start)
return func
return inner
return decorator
@計時(配料='波霸')
def 波霸奶茶():
time.sleep(1)
print("製作一杯波霸奶茶")
@計時(配料='椰果')
def 四季奶綠():
time.sleep(2)
print("製作一杯四季奶綠")
if __name__ == '__main__':
波霸奶茶()
print()
四季奶綠()
複製程式碼
執行結果如下:
加入配料:波霸
製作一杯波霸奶茶
耗時 1.0022242069244385
加入配料:椰果
製作一杯四季奶綠
耗時 2.0034420490264893
複製程式碼
③ 多個裝飾器的執行順序
一個函式可以搭配多個修飾器使用,執行的順序:「從下往上,就近原則,靠近函式定義的先執行
」。驗證程式碼示例如下:
def func_a(func):
def decorator():
func()
print("Call func_a")
return decorator
def func_b(func):
def decorator():
func()
print("Call func_b")
return decorator
def func_c(func):
def decorator():
func()
print("Call func_c")
return decorator
@func_a
@func_b
@func_c
def func_t():
pass
if __name__ == '__main__':
func_t()
複製程式碼
執行結果如下:
Call func_c
Call func_b
Call func_a
複製程式碼
④ 四種不同型別的裝飾器
Python中有四種不同型別的裝飾器,分別是:「函式裝飾函式」「函式裝飾類」「類裝飾函式」
「類裝飾類」, 和類有關的裝飾器可以等後面學完類和物件再回頭看,不用死記,用到的時候
照葫蘆畫瓢改改就好。另外類裝飾函式或類主要依賴類的__call__
函式,當使用@形式將裝飾器
附加到函式上時,就會呼叫該函式。
- 「函式裝飾函式」,使用程式碼示例如下:
def func_func(func):
def decorator(a, b):
print("函式裝飾的函式名:", func.__name__)
result = func(a, b)
return result
return decorator
@func_func
def func_add(a, b):
return a + b
if __name__ == '__main__':
print(func_add(1, 2))
複製程式碼
執行結果如下:
函式裝飾的函式名: func_add
3
複製程式碼
- 「函式裝飾類」,使用程式碼示例如下:
def func_class(cls):
def decorator(name):
print("函式裝飾的類:", cls.__name__, name)
return cls(name)
return decorator
@func_class
class A:
def __init__(self, n):
self.n = n
def show(self):
print("a = " + self.n)
a = A('123')
a.show()
複製程式碼
執行結果如下:
函式裝飾的類: A 123
a = 123
複製程式碼
- 「類裝飾函式」,使用程式碼示例如下:
class class_func:
def __init__(self, _func):
self._func = _func
def __call__(self, name):
print("類裝飾的函式名:", self._func.__name__, name)
return self._func(name)
@class_func
def func(a):
return a
if __name__ == '__main__':
print(func('123'))
複製程式碼
執行結果如下:
類裝飾的函式名: func 123
123
複製程式碼
- 「類裝飾類」,使用程式碼示例如下:
class class_class:
def __init__(self, _cls):
self._cls = _cls
def __call__(self, name):
print("類裝飾的類的類名:", self._cls.__name__, name)
return self._cls(name)
@class_class
class A:
def __init__(self, a):
self.a = a
def show(self):
print('self.a = ', self.a)
if __name__ == '__main__':
a = A('123')
a.show()
複製程式碼
執行結果如下:
類裝飾的類的類名: A 123
self.a = 123
複製程式碼
如果本文對你有所幫助,歡迎
留言,點贊,轉發
素質三連,謝謝?~