迭代是訪問集合元素的一種方式。迭代器是一個可以記住遍歷的位置的對象。迭代器對象從集合的第一個元素開始訪問,直到所有的元素被訪問完結束。迭代器只能往前不會后退。
以直接作用于 for 循環的數據類型有以下幾種:
這些可以直接作用于 for 循環的對象統稱為可迭代對象: Iterable 。
可以使用 isinstance() 判斷一個對象是否是 Iterable 對象:
from collections import Iterable
isinstance([],Iterable)
# True
isinstance({},Iterable)
# True
isinstance(123,Iterable)
# False
isinstance((x for x in range(10)),Iterable)
# True
可以被next()函數調用并不斷返回下一個值的對象稱為迭代器:Iterator。
可以使用 isinstance() 判斷一個對象是否是 Iterator 對象:
from collections import Iterator
isinstance([],Iterator)
False
isinstance({},Iterator)
False
isinstance((x for x in range(10)),Iterator) #
True
生成器都是迭代器。
雖然list 、 tuple 、 dict 、 set 、 str 等是可迭代對象,但他們不是迭代器??梢酝ㄟ^iter()函數把可迭代對象編程迭代器。
isinstance(iter([]),Iterator)
# True
isinstance(iter({}),Iterator)
# True
isinstance(iter("asdf"),Iterator)
# True
我們可以通過列表生成式來創建一個列表,但是收到內存的限制,列表的容量肯定是有限的。而且,創建一個包含100萬個元素的列表,不僅占用很大的存儲空間,如果我們僅僅需要訪問前面幾個元素,那后面絕大多數元素占用的空間都白白浪費了。所以,如果列表元素可以按照某種算法推算出來,那我們是否可以在循環的過程中不斷推算出后續的元素呢?這樣就不必創建完整的list,從而節省大量的空間。在Python中,這種一邊循環一邊計算的機制,稱為生成器:generator。
1|1修改列表推導式創建生成器的方法
最簡單的方法是把列表生成式中的 [ ] 改成 ( ) 就好了。
a = [x for x in range(10)] print(a) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] b = (x for x in range(10)) print(b) # <generator object <genexpr> at 0x03387DB0>
如何遍歷生成器
我們發現生成器不是能直接打印出來的,我們可以通過next()函數來獲得生成器的下一個返回值。
生成器保存的是算法,每次調用 next(G) ,就計算出 G 的下一個元素的值,直到計算到最后一個元素,沒有更多的元素時,拋出 StopIteration 的異常。
**使用next() 或者__next __():** print(next(b)) # 0 print(next(b)) # 1 print(next(b)) # 2 print(next(b)) # 3 print(next(b)) # 4 print(next(b)) # 5 print(b.__next__()) # 6 print(b.__next__()) # 7 print(b.__next__()) # 8 print(b.__next__()) # 9 print(b.__next__()) # Traceback (most recent call last): # File "<input>", line 2, in <module> # StopIteration
那么有什么簡單的方法呢?因為生成器是可迭代對象,也可以使用for循環來遍歷它,并且不需要關心 StopIteration 異常。
b = (x for x in range(10)) for x in b: print(x) # 0 # 1 # 2 # 3 #...
如果如果生成器推算的算法比較復雜,用類似列表生成式的 for 循環無法實現的時候,還可以用函數來實現。把你要返回的值前面加yield 即可。
使用函數實現上面代碼:
def fn(): for x in range(3): yield x # 遍歷函數實現的生成器 f = fn() print(next(f)) # 0 print(next(f)) # 1 print(next(f)) # 2 print(next(f)) # Traceback (most recent call last): # File "<input>", line 1, in <module> # StopIteration
使用生成器實現斐波拉契數列:
def fib(count): n = 0 a,b = 0,1 while n < count: yield b a,b = b,a+b n += 1 return "done" f = fib(5) print(next(f)) # 1 print(next(f)) # 1 print(next(f)) # 2 print(next(f)) # 3 print(next(f)) # 5 print(next(f)) # Traceback (most recent call last): # File "<input>", line 1, in <module> # StopIteration: done
yield執行流程
我們在循環過程中不斷調用 yield ,就會不斷中斷。當然要給循環設置一個條件來退出循環,不然就會產生一個無限數列出來。同樣的,把函數改成generator后,我們基本上從來不會用 next() 來獲取下一個返回值,而是直接使用 for 循環來迭代:
for x in fib(5): print(x) # 1 # 1 # 2 # 3 # 5
但是用for循環調用generator時,發現拿不到generator的return語句的返回值。如果想要拿到返回值,必須捕獲StopIteration錯誤,返回值包含在StopIteration的value中:
f = fib(5)
while True:
try:
print(next(f))
except StopIteration as e:
print("生成器返回值:%s"%e.value)
break
# 1
# 1
# 2
# 3
# 5
# 生成器返回值:done
1|3send方法
def gen():
i = 0
while i<3:
temp = yield i
print(temp)
i+=1
g = gen()
print(g.__next__())
# 0
print(g.send(None))
# None
# 1
print(next(g))
# None
# 2
print(g.send("哈哈")
# 哈哈
# Traceback (most recent call last):
# File "<input>", line 1, in <module>
# StopIteration
上面代碼可以看出next()、next ()、send(None)是等價的并沒有什么區別。
在函數內部再定義一個函數,并且這個函數用到了外邊函數的變量,那么將這個函數以及用到的一些變量稱之為閉包
def test(number):
def test_in(number_in):
print("test_in函數 的number_in=%s"%number_in)
return number_in+number
# 返回test_in函數的引用
return test_in
ret = test(20)
print(ret(100)) # 相當于直接調用test_in函數,并給它傳值100
# test_in函數 的number_in=100
# 120
print(ret(200))
# test_in函數 的number_in=200
# 220
在數學中,一次函數:y=kx+b,在一條確定的直線中,它的k、b是不變的。求y時,根據確定的k、b、x來求出。
def line_conf(k, b): def line(x): return k*x + b return line line1 = line_conf(1, 1) line2 = line_conf(4, 5) print(line1(5)) print(line2(5)) # 6 # 25
如果沒有閉包,我們需要每次創建直線函數的時候同時說明a,b,x。這樣,我們就需要更多的參數傳遞,也減少了代碼的可移植性。
裝飾器就是對一個函數進行裝飾,給這個函數增加額外的功能。
def logging(func):
def wrap():
print("正在打印日志!")
func()
return wrap
@logging # 該裝飾器為函數增加了打印日志的額外功能,并且之前函數內部代碼不會改變。
def login():
print("張三正在登陸。")
login()
# 正在打印日志!
# 張三正在登陸。
4|2兩個裝飾器
def makeBold(fn1):
def wrapped():
print("----1----")
return "<b>"+fn1()+"</b>"
return wrapped
def makeItalic(fn2):
def wrapped():
print("----2----")
return "<i>"+fn2()+"</i>"
return wrapped
@makeBold
@makeItalic
def f1():
print("----3----")
return "hello world"
ret = f1() # 此時f1并不是f1函數,它是makeBold裝飾器返回的wrapped函數的引用。
print(ret)
"""
輸出結果:
----1----
----2----
----3----
<b><i>hello world</i></b>
"""
調用流程:
一般情況下裝飾器內部函數的參數都是不定長參數,保證通用性,確保裝飾任何函數時都不會出錯。
def logging(func):
def wrap(*args,**kwargs):
print("正在打印日志!")
func(*args,**kwargs)
return wrap
@logging # 該裝飾器為函數增加了打印日志的額外功能,并且之前函數內部代碼不會改變。
def login(name,dic):
print("%s正在登陸。"%name)
print(dic)
login("李四",{"sex":"男"})
# 正在打印日志!
# 李四正在登陸。
# {'sex': '男'}
4|4類裝飾器
class Test(object):
def __init__(self, func):
print("---初始化---")
print("func name is %s"%func.__name__)
self.__func = func
def __call__(self):
print("---裝飾器中的功能---")
self.__func()
@Test
def test():
print("----test---")
test()
"""
輸出結果:
---初始化---
func name is test
---裝飾器中的功能---
----test---
"""
說明:
__init__方法中的func變量指向了test函數體。self.__func = func這句代碼,從而在調用__call __方法中能夠調用到test之前的函數體。總結
以上所述是小編給大家介紹的python中的生成器、迭代器、閉包、裝飾器,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對億速云網站的支持!
如果你覺得本文對你有幫助,歡迎轉載,煩請注明出處,謝謝!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。