迭代器和異常處理

Hans_Wang發表於2021-11-19

迭代器和異常處理

1. 可迭代物件

迭代:迭代即更新換代,每次的更新都必須依賴於上一次的結果.

每一次對過程的重複稱為一次“迭代”,每一次迭代得到的結果會被用來作為下一次迭代的初始值.

python中可以通過for迴圈來遍歷這個listtuple,這種遍歷我們稱為迭代(Iteration)。

迭代其實給我們提供了一種不依賴索引取值的方式

1.1 可迭代物件

內建有__iter__方法的都稱之為可迭代物件.

針對雙下滑線開頭 雙下滑線結尾的方法 最為專業標準的讀法為
雙下 方法名

查詢哪些型別是可迭代物件

# 1. 整型:
>>> i = 1   
>>> i.__iter__()	# 沒有__iter__方法,不是可迭代物件
AttributeError: 'int' object has no attribute '__iter__'
    
# 2. 浮點型:
>>> f=1.2 	
>>> f.__iter__()	# 沒有__iter__方法,不是可迭代物件
AttributeError: 'float' object has no attribute '__iter__'

# 3. 布林值
>>> bl = True
>>> bl.__iter__()	# 沒有__iter__方法,不是可迭代物件
AttributeError: 'bool' object has no attribute '__iter__'    
    
# 4. 字串
>>> str = 'abc' 
>>> str.__iter__()	# 有__iter__方法,是可迭代物件
<str_iterator object at 0x7ffa1ac28898>

# 5. 列表
>>> list1 = [1, 2, 3, 4] 
>>> list1.__iter__()	# 有__iter__方法,是可迭代物件
<list_iterator object at 0x7ffa1ac28860>

# 6. 元組
>>> tuple1 = (1, 2, 3, 4)	
>>> tuple1.__iter__()	# 有__iter__方法,是可迭代物件
<tuple_iterator object at 0x7ffa1ac286d8>

# 7. 字典
>>> dict1 = {'name':'hans', 'age':18} 
>>> dict1.__iter__()	# 有__iter__方法,是可迭代物件
<dict_keyiterator object at 0x7ffa1ac2e7c8>

# 8. 集合
>>> set1 = {1, 2, 3, 4} 
>>> set1.__iter__()		# 有__iter__方法,是可迭代物件
<set_iterator object at 0x7ffa1ac0fc60>
# 9. 檔案物件
>>> f = open(r'a.txt', 'r', encoding='utf8') 
>>> f.__iter__()
<_io.TextIOWrapper name='a.txt' mode='r' encoding='utf8'>

可迭代物件為:字串、列表、元組、字典、集合、檔案物件

其實__iter__()有更簡單的呼叫方式:iter()

# 列表:
>>> list1 = [1, 2, 3, 4] 
>>> list1.__iter__()	
<list_iterator object at 0x7ffa1ac28860>
>>> iter(list1)
<list_iterator object at 0x7ffa1ac28400>

# 字典:
>>> dict1 = {'name':'hans', 'age':18} 
>>> dict1.__iter__()	# 有__iter__方法,是可迭代物件
<dict_keyiterator object at 0x7ffa1ac2e7c8>
>>> iter(dict1)
<dict_keyiterator object at 0x7ffa1ac2e7c8>

# __iter__()和iter()返回的結果是一樣的。

2.迭代器物件

現在我們知道可迭代物件為:字串、列表、元組、字典、集合、檔案物件

但這些是可迭代物件,但不一定是迭代器物件。

因為,迭代器是:即含有__iter__方法又含有__next__方法的才叫迭代器。

檢視可迭代物件是否為迭代器:

# 1. 字串
>>> str = 'abc' 
>>> str.__next__()	# 沒有__next__方法,不是迭代器
AttributeError: 'str' object has no attribute '__next__'  
# 2. 列表
>>> list1 = [1, 2, 3, 4] 
>>> list1.__next__()	# 沒有__next__方法,不是迭代器
AttributeError: 'list' object has no attribute '__next__'
# 3. 元組
>>> tuple1 = (1, 2, 3, 4)
>>> tuple1.__next__()	# 沒有__next__方法,不是迭代器
AttributeError: 'tuple' object has no attribute '__next__'
# 4. 字典
>>> dict1 = {'name':'hans', 'age':18} 
>>> dict1.__next__()	# 沒有__next__方法,不是迭代器
AttributeError: 'dict' object has no attribute '__next__'
# 5. 集合
>>> set1 = {1, 2, 3, 4} 
>>> set1.__next__()		# 沒有__next__方法,不是迭代器
AttributeError: 'set' object has no attribute '__next__'
# 6. 檔案物件
>>> f = open(r'a.txt', 'r', encoding='utf8') 
>>> f.__next__()	# 有__next__方法,是迭代器
'Hello\n'

可迭代物件字串、列表、元組、字典、集合、檔案物件中只有檔案物件是迭代器。

檔案物件本身即是可迭代物件又是迭代器物件

雖然字串、列表、元組、字典、集合不是迭代器,但是可以使用__iter__方法轉換成迭代器

# 1. 字串
>>> str = 'abc' 
>>> s = str.__iter__()   # 將迭代物件使用__iter__方法轉成迭代器物件
>>> s.__iter__()    # s即有__iter__方法又有__next__方法
<str_iterator object at 0x7ffa1ac28c18>     
>>> s.__next__()    #  迭代器物件執行__next__方法其實就是在迭代取值(類似for迴圈)
'a'
>>> s.__next__()
'b'
>>> s.__next__()
'c'

# 2. 列表
>>> list1 = [1, 2, 3, 4] 
>>> l.__next__()
1
>>> l.__next__()
2
>>> l.__next__()
3
>>> l.__next__()
4
>>> l.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
# 同理元組、字典、集合都可以使用這種方法轉成迭代器。

迭代器物件執行__next__方法其實就是在迭代取值,但是它不知道序列的長度,所以會__next__會一直取,直到沒有資料時丟擲StopIteration錯誤。

迭代器物件無論執行多少次__iter__方法,還是迭代器物件(本身)

>>> list1 = [1, 2, 3, 4] 
>>> l = list1.__iter__()
>>> l.__iter__()
<list_iterator object at 0x7ffa1ac286a0>
>>> l.__iter__().__iter__().__iter__()  
<list_iterator object at 0x7ffa1ac286a0>

# 執行一次__iter__方法和多次執行__iter__方法,結果一樣。

練習

>>> dict1 = {'name':'hans', 'age':18} 
>>> d = dict1.__iter__()
>>> d.__iter__().__next__()  # 猜一下執行結果 
'name'
>>> d.__iter__().__next__()
'age'
# 上面就是體現了,迭代器物件無論執行多少次__iter__方法,還是迭代器物件(本身)

>>> dict1.__iter__().__next__()
>>> dict1.__iter__().__next__()
>>> dict1.__iter__().__next__()
# 猜一下上面執行的結果
# 結果:
'name'
'name'
'name'
# 每一次都會重新轉換成迭代器。然後進行一次迭代取值

3. for迴圈本質

迴圈列印出列表中每個元素, 但是不能使用for迴圈

l1 = [1,2,3,4,5,6,7,8,9,11,22,33,44,55]
res = l1.__iter__()
count = 0
while count < len(l1):
      print(res.__next__())
      count +=1
    
# 執行結果:
1
2
3
4
5
6
7
8
9
11
22
33
44
55

其實for迴圈本質就是使用迭代器

for迴圈內部原理

   1. 將關鍵字`in`後面的資料先呼叫`__iter__`方法轉為迭代器物件
   2. 迴圈執行`__next__`方法
   3. 取完之後`__next__`會報錯 但是`for`迴圈會自動捕獲該錯誤並處理

4. 異常處理

如果你的程式碼能用,就不要去碰它

4.1 什麼是異常

程式碼執行出錯會導致異常 異常發生後如果沒有解決方案則會到底整個程式結束

4.2 異常三個重要組成部分

  1. traceback
    翻到最下面從下往上的第一個藍色字型滑鼠左鍵點選即可跳轉到錯誤的程式碼所在的行

  2. xxxError

    異常的型別

  3. 異常型別冒號後面的內容

    異常的詳細原因(仔細看完之後可能就會找到解決的方法)

4.3 異常的種類

  • 語法錯誤
  • 邏輯錯誤

4.4 常見異常型別

異常 描述
AssertionError assert(斷言)語句失敗
AttributeError 試圖訪問一個物件沒有的屬性,比如foo.x ,但是foo沒有x這個屬性。
IOError 輸入/輸出異常,基本上是無法開啟檔案。
ImportError 無法引入模組或者包,基本上是路徑問題
IndentationError 語法錯誤,程式碼沒有正確對齊
IndexError 下標索引超出序列邊界,比如當x只有三個元素,卻試圖訪問x[5]
KeyError 試圖訪問字典裡不存在的鍵
KerboardInterrupt Ctrl + C 被按下
NameError 使用一個還未被賦值予物件的變數
SyntaxError Python程式碼非法,程式碼不能解釋
TypeError 傳入物件型別與要求的不符
UnboundLocalError 試圖訪問一個還未被設定的區域性變數,基本上是由於另一個同名的全域性變數,導致你以為正在訪問它
ValueError 傳入一個呼叫者不期望的值,即使值的型別是正確的

[Python官方異常列表:][https://docs.python.org/3/library/exceptions.html#exception-hierarchy ]

https://docs.python.org/3/library/exceptions.html#exception-hierarchy

4.5 異常處理基本語法結構

格式:

try:
    有可能會出錯的程式碼
except 異常型別 as e:
    出錯之後對應的處理機制(e是錯誤的詳細資訊)
except 異常型別 as e:
    出錯之後對應的處理機制(e是錯誤的詳細資訊)
except 異常型別 as e:
    出錯之後對應的處理機制(e是錯誤的詳細資訊)

示例:

# 程式碼:
f = open(r'b.txt', 'r', encoding='utf8')
print("Hello")
# 執行結果:
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: 'b.txt'
# 沒有b.txt這個檔案,程式報錯退出,後面的print("Hello")沒有執行

# 使用異常處理
# 程式碼:
try:
    f = open(r'b.txt', 'r', encoding='utf8')
except FileNotFoundError as e:
    print("沒有這個檔案,請先建立它")
print("Hello")

# 執行結果:
沒有這個檔案,請先建立它
Hello

# 雖然也報錯,但是捕獲到異常,所以程式沒有報錯退出print()依然執行

使用異常處理時,except可以寫多個,寫多個會比較麻煩,可以使用Exception它能捕獲各種異常

# 程式碼:
try:
     f = open(r'b.txt', 'r', encoding='utf8')
except Exception as e:   # 使用Exception依然能捕獲到異常
    print("沒有這個檔案,請先建立它")

# 執行結果:
沒有這個檔案,請先建立它

4.6 使用異常處理規則

  1. 有可能會出現錯誤的程式碼才需要被監測
  2. 被監測的程式碼一定要越少越好
  3. 異常捕獲使用頻率越低越好