Python函數部分3——迭代器對象 (不依賴索引取值),三元,列表,字典,生成器表達式等

一、迭代器

    1、什麼是迭代

        1.1 迭代是一個重複的過程,每一次重複都是基於上一次的結果而進行的。

        1.2 單純的重複並不是迭代

    2、爲什麼要用迭代器

       1、列表,元組,字符串都可以依靠索引把值取出來,而一些數據類型沒有索引,那麼如何取出裏面的值呢? --這裏就要用到迭代器!

       2、迭代器就是一種可以不依賴索引的迭代取值方式!

   

二、迭代器分爲:可迭代對象不可迭代對象

        2.1、可迭代的對象(數據類型)如下:     在python中,但凡帶內置有__iter__()方法的數據類型(或者對象),都是可迭代的對象! 

            1.列表類型  

            2.元組類型 

            3.集合類型  

            4.字典類型  

            5.文本類型(文本類型本身就是迭代器對象"具有__next__()"方法)            

       2.2、不可迭代的對象(數據類型)如下:  

            1.整數類型

            2.浮點數類型

 

二、迭代器對象 (通過調用可迭代對象拿到迭代器對象)  

  2.1 如何拿到一個迭代器對象?

  執行可迭代對象下的__iter__()方法,得到的返回值就是一個迭代器對象

c=['a','b']   #列表類型

res=c.__iter__()
print(res)

>>:<list_iterator object at 0x0000017A993592E8>

 

三、使用迭代器對象   迭代器對象有,內置的__next__()方法 ,同時具有__iter__()方法 (執行該方法得到的仍然是迭代器本身)

     3.1、使用迭代器對象的__next__()方法,就可以取出列表內的值,這就是通過迭代器取值。(不依賴索引取值!!)

     例子1:使用迭代器取出字典裏面的key和value

dic={'aaa':123,'bbb':456}
res_dic=dic.__iter__()

迭代獲取字典裏面的key
print(next(res_dic))  #使用迭代器對象內置__next__方法,可以取出迭代器的值(取第1個key)
print(next(res_dic))  #使用迭代器對象內置__next__方法,可以取出迭代器的值(取第2個key)

aaa
bbb

迭代獲取字典裏面的value
print(dic[next(res_dic)])   #使用迭代器對象內置__next__方法,可以取出迭代器的值(取第1個value)
print(dic[next(res_dic)])   #使用迭代器對象內置__next__方法,可以取出迭代器的值(取第2個value)

123
456

    例子2:使用迭代器取出文件裏面的內容

f=open('a.txt','rt',encoding='utf-8')
iter_obj=f.__iter__()

print(iter_obj.__next__(),end='')   #end='' 表示設置換行符爲空
print(iter_obj.__next__(),end='')   #end='' 表示設置換行符爲空
print(iter_obj.__next__(),end='')   #end='' 表示設置換行符爲空
print(iter_obj.__next__(),end='')   #end='' 表示設置換行符爲空

>>:111
>>:22222
>>:33333333
>>:44444444444

    3.2、 當字典內的值被取光了,就會報錯"StopIteration" (這是一個結束信號,表示迭代器的值被取完了),如何解決?

    1、使用try+except StopIteration的方式

dic={'k1':1,'k2':1,'k3':1}
res=dic.__iter__()

while True:
    try:
        print(res.__next__())    #使用迭代器對象內置__next__方法,可以取出迭代器的值(取第1個)
    except StopIteration:
        break

k1
k2
k3

 

四、總結迭代器對象

    1、可迭代對象不一定是迭代器對象!

         1.列表類型  2.元組類型 3.集合類型  4.字典類型  都不具備__next__()方法。

    2、迭代器對象一定是可迭代對象!

          1.文本類型(文本類型本身就是迭代器對象"具有__next__()"方法)具備__iter__()方法,同時也具備__next__()方法。

    3、總結迭代器的優缺點

         缺點:1、只能往後取值,不能往前取值,是一次性的,值取乾淨後無法再次取值,除非重新得到新的迭代器對象。

         優點:1、提供了一種不依賴於索引的迭代取值方式   2、節省內存

 

五、for 循環的底層運行機制  for循環也叫做迭代器循環

   5.1、能被for循環,循環的對象,它一定是可迭代對象!

dic={'k1':1,'k2':1,'k3':1}

for k in dic:
    print(k)

for循環的機制:

   1、先調用in後那個對象的__iter__()方法,得到該對象的迭代器對象:  ### iter_obj=dic.__iter__()
   2、執行迭代器對象的__next__()方法,將得到的返回值賦值給in前面的變量名,然後執行一次循環體代碼: ### iter_obj.__next__()
   3、循環往復,直到把dic裏面的值全部取完,然後自動捕捉"StopIteration"這個異常,結束循環。

 

六、生成器  (學習生成器是爲了掌握一種自定義迭代器的方式)

 6.1、什麼是生成器 ?

  1、在函數內但凡有yield關鍵字在調用函數時就不會執行函數體內的任何代碼得到的返回值就是一個生成器對象[一串內存地址](生成器的本質就是迭代器)。

  2、生成器本身就是迭代器。

def func():
    print('11111')
    yield 1
    print('22222')
    print('33333')

res=func()
print(res)

>>:<generator object func at 0x00000204A8F51B48>

 

6.2、使用生成器 -- 用yield來控制返回值(yield和函數內的return很相似),取到想要的值

  nxet(res)過程:

          會觸發生成器res所對應的函數體代碼的運行,直到遇到yield關鍵字,把yield後的返回值當做本次next操作的結果返回

def func():
    print('11111')
    yield 1        # 函數體代碼運行到此處時會暫停,把yield之前運行的代碼以及yield後面的1作爲返回值返回
    print('22222')
    yield 2        # 函數體代碼運行到此處時會暫停,把yield之前運行的代碼以及yield後面的2作爲返回值返回
    print('33333')
res=func()

print(next(res))   # 執行next(res)獲取到的值爲"11111",然後繼續執行代碼遇到了yield,那麼就把yield後面的1作爲本次結果的返回值
11111
1

print(next(res))  # 執行next(res)獲取到的值爲"22222",然後繼續執行代碼遇到了yield,那麼就把yield後面的2作爲本次結果的返回值
22222
2

print(next(res))
33333
StopIteration   # 由於函數內的"print('33333')"這行代碼之後無yield關鍵字,那麼next(res)在取到'33333'這個值之後,沒有其他值可以取了,就會拋出一個StopIteration異常。

for n in res:
    print(n)
11111   # 這是執行函數體代碼的結果
1       # 這是for循環變量n的值
22222   # 這是執行函數體代碼的結果
2       # 這是for循環變量n的值
33333   # 這是執行函數體代碼的結果,由於後面並沒有yield關鍵字,就會拋出一個StopIteration異常。

過程解析:

執行for循環相當於執行了:res.__next__() == next(res):
   1、 首先next(res)會執行遇到yield之前的代碼print('11111'),然後拿到"yield 1"把1最爲本次結果的返回值賦值給n,然後打印n,那麼第一次循環結束。
   2、然後執行next(res),執行函數內"yield 1"之後的代碼,直至遇到下一個"yield"關鍵字。

小例子:自定義函數模擬range(1,10,2)   

def my_range(start,stop,step=1):
    print('開始')
    while start < stop:
        yield start
        start+=step
    print('結束')

for n in my_range(1,10,2):
    print(n)

開始
1
3
5
7
9
結束

 

6.3、生成器如何運行?

  1、首先要明白生成器本身就是迭代器,那麼運行生成器就可以按照迭代器的方式去運行!

  2、運行生成器就會觸發迭代器所對應的代碼,並開始運行迭代器的代碼。

  3、運行迭代器的代碼時,只要碰到yield,代碼就會停止運行,並拿到yield 的返回值。

 

6.4、yield關鍵字的另外一種使用形式:表達式形式的yield應用

    1、先讓函數運行起來,然後暫停到某一處,然後傳一個值運行一次代碼,傳一個值運行一次代碼。

   通過:生成器.send的方法給yield 傳值,例子如下:[執行g.send就相當於給yield傳值,同時執行一次next(g)]


def dog(dog_name):
    print('狗準備開吃!')
    food_list=[]
    while True:
        print(1)
        food=yield food_list   # food=yield='骨頭'
        print('狗:%s,吃了%s' %(dog_name,food))
        food_list.append(food)
        print(2)

g=dog('doudou')     #拿到一個迭代器
next(g)             #next(g)==g.send(None),開始運行函數體代碼,next(res)==res.__next__()
                    #強調:對於表達式形式的yield的生成器,在使用前必須先用next(g)或者g.send(None)初始化一次。
res2=g.send('骨頭')
print(res2)
res3=g.send('骨頭1')
print(res3)


狗準備開吃
1
[]
dog: doudou 吃了 骨頭
2
1
['骨頭']
dog: doudou 吃了 骨頭1
2
1
['骨頭', '骨頭1']

註釋:

1、yield後面的 food_list 在沒有沒傳值的情況下,默認爲None。

2、執行一次g.send('骨頭')後,會把'骨頭'傳值給 yield ,並執行一次"yield"關鍵字之後的函數體代碼,直到遇見下一個“yield”爲止。

3、如上例子,當g.send('骨頭')後,循環一遍結束,直到遇見一個新的yield爲止。此時 food_list 的值就爲None(默認值)。

 

七、面向過程編程

  1、什麼是面向過程編程?

   核心是過程二字,過程指的是解決問題的步驟,即先幹什麼,在幹什麼,在幹什麼。。。。

   基於面向過程的思想,編寫程序就好比在設計一條流水線,是一種機械式思維方式。

 

  優點:將複雜的問題流程化,進而簡單化。

  缺點:修改了某一個階段,其他相關聯的階段都有可能受到影響,牽一髮而動全身,擴展性極差。

  應用:應用於對擴展性要求不高的場景。

 

面向過程編程小例子:把要做的事情拆分爲一件件小的事情,然後完成。

def login():
    uname=input('pls input uname>>:').strip()
    pwd=input('pls input pwd>>:').strip()
    return uname,pwd

def auth(uname,pwd):
    if uname == 'szq' and pwd == '123':
        return True,uname
    else:
        return False,uname

def index():
    if res[0]:
        print('歡迎%s登錄' %res[1])
    else:
        print('%s登錄失敗' % res[1])

uname,pwd=login()
res=auth(uname,pwd)
index()

 

八、三元表達式

x=10
y=30

res=x if x >y else y   #如果x的值大於y的值,那麼返回x,否則返回y的值。
print(res)

30

 

九、列表生成式,字典生成式,生成器表達式

 當數據量不多的時候,用列表表達式

 1、列表生成式 -- 直接將產生的結果放到列表裏面

list=[n for n in range(10)]   # 每一次for循環取到的值,都會賦值給給左邊的n,然後放在列表list裏面,然後接着下一次循環
print(list)

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

 for 循環後接if判斷

list=[n for n in range(10) if n > 3]   # 每一次for循環取到的值(然後執行右邊的if判斷,符合要求的值),都會賦值給給左邊的n,然後放在列表list裏面,然後接着下一次循環
print(list)

[4, 5, 6, 7, 8, 9]

 例子1:將小寫全部改爲大寫

names=['aaa','bbb','ccc','ddd']
names=[name.upper() for name in names]
print(names)

['AAA', 'BBB', 'CCC', 'DDD']

 例子2:將列表內以'sb'結尾的字符去除,並統計剩下字符的長度

names=['aaa','bbb_sb','ccc_sb','ddd']
names=[len(name) for name in names if not name.endswith('sb')]
print(names)

[3, 3]

 

2、字典生成式

 例子:將字典裏面的元組改爲key:value的形式

dic={('aaa','123'),('bbb','223'),('ccc','323')}
dic={k:y for k,y in dic}
print(dic)

{'aaa': '123', 'ccc': '323', 'bbb': '223'}

 

3、生成器表達式  -- 拿到一個生成器對象,需要next(g)一次取一次值。

當數據量特別多的時候用,用生成器表達式

l=(n for n in range(10))
print(next(l))
print(next(l))
print(next(l))

0
1
2

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章