python基礎--迭代器、生成器

(1)迭代器

可迭代對象和迭代器的解釋以下:python

'''
什麼是對象?Python中一切皆對象,以前咱們講過的一個變量,一個列表,一個字符串,文件句柄,函數名等等均可稱做一個對象,其實一個對象就是一個實例,就是一個實實在在的東西。那麼什麼叫迭代?其實咱們在平常生活中常常遇到迭代這個詞兒,更新迭代等等,迭代就是一個重複的過程,可是不能是單純的重複(若是隻是單純的重複那麼他與循環沒有什麼區別)每次重複都是基於上一次的結果而來。好比你爹生你,你生你爹,哦不對,你生你兒子,你兒子生你孫子等等,每一代都是不同的;還有你使用過得app,微信,抖音等,隔一段時間就會基於上一次作一些更新,那麼這就是迭代。可迭代對象從字面意思來講就是一個能夠重複取值的實實在在的東西。
'''

'''
1.可迭代對象:
字面意思:對象?python中一切皆對象。一個實實在在存在的值,就是對象
可迭代?:更新迭代。重複的,循環的一個過程,更新迭代每次都有新的內容。
能夠進行循環更新的一個實實在在的值。
專業角度:可迭代對象?內部含有__iter__方法的對象,可迭代對象。
目前學過的可迭代對象?str list tuple dict set range 文件句柄

2.獲取對象的方法
dir()
3.判斷一個對象是不是可迭代的對象
s1 = 'adasda'
l1 = [1, 2, 3]
print(dir(s1)) # 獲取全部的方法。
print(dir(l1))  # 獲取列表的全部的方法。
print('__iter__' in dir(s1))  # True
print('__iter__' in dir(range(10)))  # True
# 輸出的結果:True表示是可迭代對象,False表示不是可迭代對象。

4.小結
字面意思:能夠進行循環更新的一個實實在在的值。
專業角度:內部含有__iter__方法的對象,表示是可迭代對象。
判斷一個對象是否是可迭代對象:__iter__方法是否在dir(對象)中。
str list tuple dict set range
優勢:1.存儲的數據直接能顯示,比較直觀。2.擁有的方法比較多,操做方便。
缺點:1.佔用內存。2.不能直接經過for循環,不能直接取值(索引,key除外)。可是在實際的時候咱們列表和字符串等等可迭代對象能夠經過for循環取值,是由於咱們封裝了__next__方法,變成了迭代器。那麼就能夠直接經過for循環取值。

5.迭代器
(1)迭代器的定義
    字面意思:更新迭代,器:工具。可更新迭代的工具
    專業角度:內部含有__iter__方法而且含有__next__方法的對象就是迭代器。
(2)判斷一個對象是不是是迭代器
    __iter__方法 and __next__方法是否在不在dir(對象)中
    咱們目前學的迭代器只有文件句柄。

6.可迭代對象如何轉換爲迭代器
iter()將可迭代對象轉換爲迭代器---》iter([1,2,3])
s1 = 'asdasdcsadfc'
obj = iter(s1)  # s1.__iter__()
print(obj) # <str_iterator object at 0x0000022ED5F3CF08>

print(next(obj))
# next(obj)
print(obj.__next__())
# obj.__next__()

迭代器的取值
    s1 = 'sdwfe'
obj = iter(s1) # 這裏將可迭代對象轉換爲迭代器
print(obj)  # <str_iterator object at 0x000001F7ACF4CA88>
print(next(obj))  # s
print(next(obj))  # d
print(next(obj))  # w
print(next(obj))  # f
print(next(obj))  # e
# print(next(obj))  # StopIteration

# Traceback (most recent call last):
#   File "D:/Program Files (x86)/DjangoProjects/basic/day11/04 迭代器.py", line 96, in <module>
#     print(next(obj))
# StopIteration
# 迭代器利用next取值:一個next取對應的一個值,若是迭代器裏面的值取完了,還要next,
# 那麼就報StopIteration的錯誤。

l1 = [11, 22, 33, 44, 55, 66]
obj = iter(l1)  # 對列表進行操做造成了迭代器
print(next(obj))
print(next(obj))
print(next(obj))
print(next(obj))
print(next(obj))
print(next(obj))
輸出的結果爲:
11
22
33
44
55
66
'''

'''
小結
	(1)可迭代對象:
	字面意思:能夠進行循環更新的一個實實在在的值。
	專業角度:內部含有__iter__方法的對象,表示是可迭代對象。
	判斷一個對象是否是可迭代對象:__iter__方法是否在dir(對象)中。
	str list tuple dict set range
	優勢:1.存儲的數據直接能顯示,比較直觀。2.擁有的方法比較多,操做方便。
	缺點:1.佔用內存。2.不能直接經過for循環,不能直接取值(索引,key除外)。可是在實際的時候	 咱們列表和字符串等等可迭代對象能夠經過for循環取值,是由於咱們封裝了__next__方法,變成了迭代器。那麼就能夠直接經過for循環取值。	
	(2)迭代器:
    字面意思:更新迭代,器:工具。可更新迭代的工具
    專業角度:內部含有__iter__方法而且含有__next__方法的對象就是迭代器。
    優勢:1.節省內存。迭代器在內存中至關於只佔一個數據的空間:由於每次取值都上一條數據會在內存釋放,加載當前的此條數據。2.惰性機制。next一次,取一個值,毫不過多取值。
    缺點:1.速度慢:用時間換空間。2.不走回頭路,不直觀。3.操做方法單一。
可迭代對象與迭代器的對比
    (幾百萬個對象,8G內存是能夠接受的,承受的)
    可迭代對象是一個操做方法比較多,比較直觀的,存儲數據相對少的一個數據集。
    當你側重於對於數據能夠靈活處理,而且內存空間足夠,將數據集設置爲可迭代對象是明確的選擇
    當你的數據量大,大到足以撐爆你的內存或者你以節省內存爲首選因素時,將數據集
    設置爲一個迭代器是一個不錯的選擇。
'''
l1 = [1, 2, 3, 4, 5, 6]
obj = iter(l1)
# obj = l1.__iter__()

for i in range(2):
    print(next(obj))

for i in range(2):
    print(next(obj))
# 輸出的結果爲:
# 1
# 2
# 3
# 4
# while循環模擬for循環(這個是面試題,直接手寫面試題)
# 剛纔咱們提到了,for循環的循環對象必定要是可迭代對象,可是這不意味着可迭代對象就能夠取值,由於for循環的內部機制是:將可迭代對象轉換成迭代器,而後利用next進行取值,最後利用異常處理處理StopIteration拋出的異常。
l1 = [1, 2, 3, 4, 5, 6]
# 1 將可迭代對象轉化成迭代器
obj = iter(l1)
# 2,利用while循環,next進行取值
while 1:
    # 3,利用異常處理終止循環
    try:
        print(next(obj))
    except StopIteration:
        break

咱們今天比較深刻的瞭解了可迭代對象與迭代器,接下來咱們說一下這二者之間比較與應用:面試

可迭代對象:是一個私有的方法比較多,操做靈活(好比列表,字典的增刪改查,字符串的經常使用操做方法等),比較直觀,可是佔用內存,並且不能直接經過循環迭代取值的這麼一個數據集。微信

應用:當你側重於對於數據能夠靈活處理,而且內存空間足夠,將數據集設置爲可迭代對象是明確的選擇。app

迭代器:是一個很是節省內存,能夠記錄取值位置,能夠直接經過循環+next方法取值,可是不直觀,操做方法比較單一的數據集。函數

應用:當你的數據量過大,大到足以撐爆你的內存或者你以節省內存爲首選因素時,將數據集設置爲迭代器是一個不錯的選擇。(可參考爲何python把文件句柄設置成迭代器)。工具

(2)生成器

什麼是生成器?這個概念比較模糊,各類文獻都有不一樣的理解,可是核心基本相同。生成器的本質就是迭代器,在python社區中,大多數時候都把迭代器和生成器是作同一個概念。不是相同麼?爲何還要建立生成器?生成器和迭代器也有不一樣,惟一的不一樣就是:迭代器都是Python給你提供的已經寫好的工具或者經過數據轉化得來的,(好比文件句柄,iter([1,2,3])。生成器是須要咱們本身用python代碼構建的工具。最大的區別也就如此了。code

'''
生成器:python社區,生成器和迭代器當作是一種。
區別:生成器是咱們本身用python代碼構建的數據和工具。
迭代器都是提供的,或者轉化得來的。迭代器都是Python給你提供的已經寫好的工具或者經過數據轉化得來的。

獲取生成器的三種方式:
1.生成器函數(本身寫的)
2.生成器表達式(本身寫的)
3.python內部提供的一些內置函數,獲得的值就是一個生成器

獲取迭代器的方式:
1.python提供的,文件句柄
2.經過可迭代對象轉換的,-iter()方法
3.python內部提供的一些內置函數,獲得的值就是一個迭代器
'''

咱們先來研究經過生成器函數構建生成器。對象

1.yield的用法索引

首先,咱們先看一個很簡單的函數:ip

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("111")
    yield 222
    print("333")
    yield 444
gener = func()
ret = gener.__next__()
print(ret)
ret2 = gener.__next__()
print(ret2)
ret3 = gener.__next__()
# 最後⼀個yield執⾏完畢. 再次__next__()程序報錯
print(ret3)
結果:
111
222
333
444

def eat():
    for i in range(1, 10000):
        yield '包子' + str(i)
e = eat()
for i in range(200):
     print(next(e))

for i in range(300):
     print(next(e))

當程序運行完最後一個yield,那麼後面繼續運行next()程序會報錯,一個yield對應一個next,next超過yield數量,就會報錯,與迭代器同樣。

yield與return的區別:

​ return通常在函數中只設置一個,他的做用是終止函數,而且給函數的執行者返回值。

​ yield在生成器函數中可設置多個,他並不會終止函數,next會獲取對應yield生成的元素。

2.yield from

在python3中提供一種能夠直接把可迭代對象中的每個數據做爲生成器的結果進行返回

# 對比yield 與 yield from 
def func():
    lst = ['衛龍','老冰棍','北冰洋','牛羊配']
    yield lst
g = func()
print(g)
print(next(g))  # 只是返回一個列表

def func():
    lst = ['衛龍','老冰棍','北冰洋','牛羊配']
    yield from lst
g = func()
print(g)
# 他會將這個可迭代對象(列表)的每一個元素當成迭代器的每一個結果進行返回。
print(next(g))
print(next(g))
print(next(g))
print(next(g))
'''
yield from ['衛龍','老冰棍','北冰洋','牛羊配'] 
等同於:
    yield '衛龍'
    yield '老冰棍'
    yield '北冰洋'
    yield '牛羊配'
'''

'''
輸出的結果爲:
<generator object func at 0x000001A7383760C8>
['衛龍', '老冰棍', '北冰洋', '牛羊配']
<generator object func at 0x000001A7383762C8>
衛龍
老冰棍
北冰洋
牛羊配
'''

有個小坑,yield from 是將列表中的每個元素返回,因此 若是寫兩個yield from 並不會產生交替的效果

def func():
    lst1 = ['衛龍','老冰棍','北冰洋','牛羊配']
    lst2 = ['饅頭','花捲','豆包','大餅']
    yield from lst1
    yield from lst2
    
g = func()
for i in g:
    print(i)
'''
衛龍
老冰棍
北冰洋
牛羊配
饅頭
花捲
豆包
大餅
'''

列表推導式

# 列表推導式
# 用一行代碼去構建一個比較複雜有規律的列表
# l1 = []
# for i in range(1,11):
#     l1.append(i)
# print(l1) # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# 列表推導式(循環模式)
l1 = [i for i in range(1, 11)]
print(l1)  # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# 列表推導式分爲兩類
# 1.循環模式:[變量(加工後的變量)for 變量 in iterable]
# 2.篩選模式:[變量(加工後的變量)for 變量 in iterable if條件]
# 循環模式
# l1 = [i for i in range(1,11)]
# print(l1) # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

將10之內全部整數的平方寫入列表。
l1 = [i*i for i in range(1,11)]
print(l1)
 
100之內全部的偶數寫入列表.
l1 = [i for i in range(2,101,2)]
print(l1)
 
從python1期到python100期寫入列表lst
lst = [f'python{i}' % i for i in range(1,19)]
print(lst)

將這個列表中大於3的元素留下來。
l1 = [4, 3, 2, 6, 5, 5, 7, 8] 
print([i for i in l1 if i > 3])

三十之內能夠被三整除的數。
multiples = [i for i in range(30) if i % 3 is 0]
print(multiples)
 
過濾掉長度小於3的字符串列表,並將剩下的轉換成大寫字母
l = ['wusir', 'laonanhai', 'aa', 'b', 'taibai']
# print([i.upper() for i in l if len(i) > 3])
找到嵌套列表中名字含有兩個‘e’的全部名字(有難度)

names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
         ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
print([name for lst in names for name in lst if name.count('e') >= 2])  # 注意遍歷順序,這是實現的關鍵
# 列表推導式基本上講完了,固然今天會作一些有關列表推導式的題,讓你們更加深刻的瞭解。

咱們再來研究經過生成器表達式構建生成器。

生成器表達式和列表推導式的語法上如出一轍,只是把[]換成()就好了。好比將十之內全部數的平方放到一個生成器表達式中。

gen = (i**2 for i in range(10))
print(gen)
# 結果: <generator object <genexpr> at 0x0000026046CAEBF8>

生成器表達式也能夠進行篩選

# 獲取1-100內能被3整除的數
gen = (i for i in range(1,100) if i % 3 == 0)
for num in gen:
    print(num)

生成器表達式和列表推導式的區別:

  1. 列表推導式比較耗內存,全部數據一次性加載到內存。而.生成器表達式遵循迭代器協議,逐個產生元素。

  2. 獲得的值不同,列表推導式獲得的是一個列表.生成器表達式獲取的是一個生成器

  3. 列表推導式一目瞭然,生成器表達式只是一個內存地址。

不管是生成器表達式,仍是列表推導式,他只是Python給你提供了一個相對簡單的構造方式,由於使用推導式很是簡單,因此大多數都會爲之着迷,這個必定要深重,推導式只能構建相對複雜的而且有規律的對象,對於沒有什麼規律,並且嵌套層數比較多(for循環超過三層)這樣就不建議你們用推導式構建。

生成器的惰性機制: 生成器只有在訪問的時候才取值,說白了.你找他要纔給你值.不找他要.他是不會執行的.

字典推導式

根據名字應該也能猜到,推到出來的是字典

lst1 = ['jay','jj','meet']
lst2 = ['周杰倫','林俊杰','郭寶元']
dic = {lst1[i]:lst2[i] for i in range(len(lst1))}
print(dic)

集合推導式

集合推導式能夠幫咱們直接生成一個集合,集合的特色;無序,不重複 因此集合推導式自帶去重功能

lst = [1,2,3,-1,-3,-7,9]
s = {abs(i) for i in lst}
print(s) # {1, 2, 3, 7, 9}
相關文章
相關標籤/搜索