- 字面意思:python
- 對象:在Python中一切皆對象。就是一個實實在在的值。git
- 可迭代:更新迭代,重複的,循環的一個過程,更新迭代每次都有新的內容。api
- 可迭代對象: 能夠進行循環更新的一個實實在在的值。安全
- 專業角度:可迭代對象就是內部含有__iter__ 方法的對象。閉包
str1 = "hello " print(dir(str1)) # 輸出結果: ['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__',
'__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__',
'__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__',
'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find',
'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier',
'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust',
'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition',
'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
st1 = "hello" print("__iter__" in dir(st1)) # True lis = [11,22,33] print("__iter__" in dir(lis)) # True
- 字面意思:能夠進行循環更新的一個實實在在的值。app
- 專業角度:內部還有__iter__ 方法的對象就是可迭代對象。ssh
- 判斷一個對象是否是可迭代對象: ide
__iter__ in dir(對象);
- 優勢:函數
1. 存儲的數據能顯示,比較直觀。工具
2. 擁有的方法比較多,操做方便。
- 缺點:
1. 佔用內存
2. 不能直接經過for循環嗎,不能直接取值(索引,key)
- 字面意思: 可更新迭代的工具
- 專業角度:內部還有__iter__方法而且含有__next__ 方法的對象就是迭代器
- 判斷是不是迭代器:
'__iter__' and '__next__' in dir(對象)
with open("a.txt",mode="w",encoding="utf-8") as f: print(("__iter__"in dir(f)) and ("__next__"in dir(f))) # True
lis = [11,22,]
iter(lis)
lis = [11,22,33,44,55,66,77,88,99,14,12,15] obj = iter(lis) # 將可迭代對象轉化成迭代器 while True: try: print(next(obj)) except StopItertion: break
- 字面意思:可更新迭代的工具
- 專業角度:內部還有__iter__ 方法而且含有__next__方法的對象就是迭代器
優勢:
1.節省內存
2. 惰性機制,next一次,取一次值
缺點:
1.速度慢
2.不走回頭路
1. 可迭代對象是一個操做方法比較多,比較直觀,存儲數據相對少(幾百萬個對象,8G內存是能夠承受的)的一個數據集。 2. 當你側重於對於數據能夠靈活處理,而且內存空間足夠,將數據集設置爲可迭代對象是明確的選擇。 3. 迭代器是一個很是節省內存,能夠記錄取值位置,能夠直接經過循環+next方法取值,可是不直觀,操做方法比較單一的數據集。 4. 當你的數據量過大,大到足以撐爆你的內存或者你以節省內存爲首選因素時,將數據集設置爲迭代器是一個不錯的選擇。
什麼是生成器?這個概念比較模糊,各類文獻都有不一樣的理解,可是核心基本相同。生成器的本質就是迭代器,在python社區中,大多數時候都把迭代器和生成器是作同一個概念。不是相同麼?爲何還要建立生成器?生成器和迭代器也有不一樣,惟一的不一樣就是:迭代器都是Python給你提供的已經寫好的工具或者經過數據轉化得來的,(好比文件句柄,iter([1,2,3])。生成器是須要咱們本身用python代碼構建的工具。最大的區別也就如此了。
1.生成器函數
2. 生成器推導式
3. Python內部提供的(內置函數或者模塊)
咱們先來研究經過生成器函數構建生成器
首先,咱們先看一個簡單的函數
def func(): print(11) return 22 ret = func() print(ret) # 運行結果: 11 22
將函數中的return換成yield,這樣func就不是函數了,而是一個生成器函數
def func(): print(11) yield 22
咱們這樣寫沒有任何的變化,這是爲何呢? 咱們來看看函數名加括號獲取到的是什麼?
def func(): print(11) yield 22 ret = func() print(ret) # 運行結果: <generator object func at 0x000001A575163888>
運行結果爲何不同呢?因爲函數中存在yield,那麼這個函數就是一個生成器函數.
當咱們再次執行這個函數的時候,就再也不是函數的執行了,而是獲取這個生成器對象,那麼生成器對象如何取值呢?
以前咱們說了,生成器的本質就是迭代器.迭代器如何取值,生成器就如何取值。因此咱們能夠直接執行next()來執行如下生成器
def func(): print("111") yield 222 gener = func() # 這個時候函數不會執⾏. ⽽是獲取到⽣成器 ret = gener.__next__() # 這個時候函數纔會執⾏ print(ret) # 而且yield會將func生產出來的數據 222 給了 ret。 # 執行結果: 111 222
生成器函數中能夠寫多個yield。
def func(): print(123) print(666) yield 222 a = 1 b = 2 c = a + b print(c) yield 88 ret = func() print(next(ret)) print(next(ret)) # 執行結果 # 123 # 666 # 222 # 3 # 88 # 一個next 對應一個yield
yield和return 的區別:
return:通常在函數中只能設置一個,它的做用是終止函數,而且把返回值給函數的調用者。
yield:只要函數中有yield,那麼就是生成器函數。生成器函數中能夠存在多個yield,yield不會終止函數,一個yield對應一個next。
舉例:吃包子練習
咱們來看一下這個需求:向樓下賣包子的老闆訂購了1000個包子.包子鋪老闆很是實在,一下就所有都作出來了
def eat(): lst = [] for i in range(1, 1000): lst.append('包子' + str(i)) return lst e = eat() print(e)
這樣作沒有問題,只吃了200個左右,剩下的800個,就只能佔着必定的空間,放在一邊了。若是包子鋪老闆效率夠高,我吃一個包子,你作一個包子,那麼這就不會佔用太多空間存儲了,完美。
def eat(): for i in range(1,1000): yield '包子'+str(i) e = eat() for i in range(200): next(e)
這二者的區別:
第一種是直接把包子所有作出來,佔用內存。
第二種是吃一個生產一個,很是的節省內存,並且還能夠保留上次的位置。
def eat(): for i in range(1,10000): yield '包子'+str(i) e = eat() for i in range(200): next(e) for i in range(300): next(e) # 屢次next包子的號碼是按照順序記錄的。
在python3中提供一種能夠直接把可迭代對象中的每個數據做爲生成器的結果進行返回
# 對比yield和yield from的區別 def func(): lis = [1,2,3,4,5] yield lis ret = func() print(next(ret)) # 返回一個列表 # 執行結果: # [1,2,3,4,5]
def func(): l1 = [1, 2, 3, 4, 5] yield from l1 ret = func() print(ret) # 他會將這個可迭代對象(列表)的每一個元素當成迭代器的每一個結果進行返回。 for i in range(5): print(next(ret)) # 執行結果 # 1 # 2 # 3 # 4 # 5
有個小坑,yield from 是將列表中的每個元素返回,因此 若是寫兩個yield from 並不會產生交替的效果
def func(): l1 = [1, 2, 3, ] lis1 = [7, 8, 9] yield from l1 yield from lis1 ret = func() print(ret) for i in range(6): print(next(ret)) # 執行結果: 1 2 3 7 8 9
從更深層的角度去理解yield from 有兩個做用
1. 他能夠徹底代替內存循環,提升效率,讓代碼讀起來更加順暢
2. 還能夠建立通道,把內存生成器直接與外層生成器的客服端鏈接起來
1. 閉包只能存在嵌套函數中
2. 內層函數對外層函數非全局變量的引用,就會造成閉包
3. 被引用的非全局變量也稱自由變量,這個自由變量與內層函數產生一個綁定關係。
4. 自由變量不會再內存中消失
保證局部信息不被銷燬,保證數據的安全性。
如何判斷一個嵌套函數是否是閉包
1.閉包只能存在嵌套函數中
2. 內層函數對外層函數非全局變量的引用(使用),就會造成閉包。
def func(): a = 1 def inner(): print(a) return inner ret = func() # 是閉包 a = 2 def func(): def inner(): print(a) return inner ret = func() # 不是閉包,a是全局變量 def func(a,b): def inner(): print(a) print(b) return inner a = 2 b = 3 ret = func(a,b) print(ret()) 就至關於這樣 def func(a,b): a = 2 b = 3 def inner(): print(a) print(b) return inner ret = func(a,b) print(ret())
def func(a,b): def inner(): print(a) print(b) return inner a = 2 b = 3 ret = func(a,b) print(ret.__code__.co_freevars)
# ('a', 'b')