01.16 單例、異常、eval函數

一、單例

01. 單例設計模式

設計模式 設計模式前人工作的總結和提煉,通常,被人們廣泛流傳的設計模式都是針對 某一特定問題 的成熟的解決方案 使用 設計模式 是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性單例設計模式 目的 —— 讓 創建的對象,在系統中 只有 唯一的一個實例 每一次執行 類名() 返回的對象,內存地址是相同的

單例設計模式的應用場景

音樂播放 對象回收站 對象打印機 對象……

02. __new__ 方法

使用 類名() 創建對象時, Python 的解釋器 首先 會 調用 __new__ 方法為對象 分配空間__new__ 是一個 由 object 基類提供的 內置的靜態方法,主要作用有兩個: 1) 在內存中為對象 分配空間 2) 返回 對象的引用Python 的解釋器獲得對象的 引用 後,將引用作為 第一個參數,傳遞給 __init__ 方法

重寫 __new__ 方法 的代碼非常固定!

重寫 __new__ 方法 一定要 return super().__new__(cls)否則 Python 的解釋器 得不到 分配了空間的 對象引用就不會調用對象的初始化方法注意: __new__ 是一個靜態方法,在調用時需要 主動傳遞 cls 參數

<code> class MusicPlayer(object):



def __new__(cls, *args, **kwargs):
# 如果不返回任何結果,
return super().__new__(cls)

def __init__(self):
print("音樂播放器初始化")


yunplayer = MusicPlayer()
print(yunplayer)/<code>

03. Python 中的單例

單例 —— 讓 類 創建的對象,在系統中 只有 唯一的一個實例

定義一個 類屬性,初始值是 None,用於記錄 單例對象的引用重寫 __new__ 方法如果 類屬性 is None,調用父類方法分配空間,並在類屬性中記錄結果返回 類屬性 中記錄的 對象引用

<code> class MusicPlayer(object):

# 定義類屬性記錄單例對象引用
instance = None


def __new__(cls, *args, **kwargs):
if cls.instance is None:
return super().__new__(cls)

return cls.instance/<code>

只執行一次初始化工作

在每次使用 類名() 創建對象時, Python 的解釋器都會自動調用兩個方法: __new__分配空間__init__對象初始化在上一小節對 __new__ 方法改造之後,每次都會得到 第一次被創建對象的引用但是:初始化方法還會被再次調用

需求

初始化動作 只被 執行一次

解決辦法

定義一個類屬性 init_flag 標記是否 執行過初始化動作,初始值為 False在 __init__ 方法中,判斷 init_flag,如果為 False 就執行初始化動作然後將 __init__ 設置為 True這樣,再次 自動 調用 __init__ 方法時,初始化動作就不會被再次執行

<code> class MusicPlayer(object):



# 記錄第一個被創建對象的引用
instance = None
# 記錄是否執行過初始化動作
init_flag = False

def __new__(cls, *args, **kwargs):
# 1.判斷類屬性是否為空對象
if cls.instance is None:
# 2.調用父類方法,為第一個對象分配空間
return super().__new__(cls)
# 3.返回類屬性保存的對象引用
return cls.instance

def __init__(self):
if not MusicPlayer.init_flag:
print("音樂播放器初始化")

MusicPlayer.init_flag = True


# 創建對個對象
player1 = MusicPlayer()
print(player1)
player2 = MusicPlayer()
print(player2)/<code>

二、異常

01. 異常的概念

程序在運行時,如果 Python 解釋器 遇到 到一個錯誤,會停止程序的執行,並且提示一些錯誤信息,這就是
異常程序停止執行並且提示錯誤信息 這個動作,我們通常稱之為:拋出(raise)異常

程序開發時,很難將 所有的特殊情況 都處理的面面俱到,通過 異常捕獲 可以針對突發事件做集中的處理,從而保證程序的 穩定性和健壯性

02. 捕獲異常

2.1 簡單的捕獲異常語法

在程序開發中,如果 對某些代碼的執行不能確定是否正確,可以增加 try(嘗試) 來 捕獲異常捕獲異常最簡單的語法格式:

<code>try:
嘗試執行的代碼
except:
出現錯誤的處理/<code>try 嘗試,下方編寫要嘗試代碼,不確定是否能夠正常執行的代碼except 如果不是,下方編寫嘗試失敗的代碼

簡單異常捕獲演練 —— 要求用戶輸入整數

<code>try:
# 提示用戶輸入一個數字
num = int(input("請輸入數字:"))
except:
print("請輸入正確的數字")/<code>

2.2 錯誤類型捕獲

在程序執行時,可能會遇到 不同類型的異常,並且需要 針對不同類型的異常,做出不同的響應,這個時候,就需要捕獲錯誤類型了語法如下:

<code>try:
# 嘗試執行的代碼
pass
except 錯誤類型1:
# 針對錯誤類型1,對應的代碼處理
pass
except (錯誤類型2, 錯誤類型3):
# 針對錯誤類型2 和 3,對應的代碼處理
pass
except Exception as result:
print("未知錯誤 %s" % result)/<code>當 Python 解釋器 拋出異常 時,最後一行錯誤信息的第一個單詞,就是錯誤類型

異常類型捕獲演練 —— 要求用戶輸入整數

需求

提示用戶輸入一個整數使用 8 除以用戶輸入的整數並且輸出

<code>try:
num = int(input("請輸入整數:"))
result = 8 / num
print(result)
except ValueError:


print("請輸入正確的整數")
except ZeroDivisionError:
print("除 0 錯誤")/<code>

捕獲未知錯誤

在開發時,要預判到所有可能出現的錯誤,還是有一定難度的如果希望程序 無論出現任何錯誤,都不會因為 Python 解釋器 拋出異常而被終止,可以再增加一個 except

語法如下:

<code>except Exception as result:
print("未知錯誤 %s" % result)/<code>

2.3 異常捕獲完整語法

在實際開發中,為了能夠處理複雜的異常情況,完整的異常語法如下: 提示: 有關完整語法的應用場景,在後續學習中,結合實際的案例會更好理解現在先對這個語法結構有個印象即可

<code>try:
# 嘗試執行的代碼
pass
except 錯誤類型1:
# 針對錯誤類型1,對應的代碼處理
pass
except 錯誤類型2:
# 針對錯誤類型2,對應的代碼處理


pass
except (錯誤類型3, 錯誤類型4):
# 針對錯誤類型3 和 4,對應的代碼處理
pass
except Exception as result:
# 打印錯誤信息
print(result)
else:
# 沒有異常才會執行的代碼
pass
finally:
# 無論是否有異常,都會執行的代碼
print("無論是否有異常,都會執行的代碼")/<code>else 只有在沒有異常時才會執行的代碼finally 無論是否有異常,都會執行的代碼之前一個演練的 完整捕獲異常 的代碼如下:

<code>try:
num = int(input("請輸入整數:"))
result = 8 / num
print(result)
except ValueError:
print("請輸入正確的整數")
except ZeroDivisionError:
print("除 0 錯誤")
except Exception as result:
print("未知錯誤 %s" % result)
else:
print("正常執行")
finally:
print("執行完成,但是不保證正確")/<code>

03. 異常的傳遞

異常的傳遞
—— 當 函數/方法 執行 出現異常,會 將異常傳遞 給 函數/方法 的 調用一方如果 傳遞到主程序,仍然 沒有異常處理,程序才會被終止

  提示

在開發中,可以在主函數中增加 異常捕獲而在主函數中調用的其他函數,只要出現異常,都會傳遞到主函數的 異常捕獲 中這樣就不需要在代碼中,增加大量的 異常捕獲,能夠保證代碼的整潔

需求

定義函數 demo1() 提示用戶輸入一個整數並且返回定義函數 demo2() 調用 demo1()在主程序中調用 demo2()

<code>def demo1():
return int(input("請輸入一個整數:"))


def demo2():
return demo1()



try:
print(demo2())
except ValueError:
print("請輸入正確的整數")
except Exception as result:
print("未知錯誤 %s" % result)/<code>

04. 拋出 raise 異常

4.1 應用場景

在開發中,除了 代碼執行出錯 Python 解釋器會 拋出 異常之外還可以根據 應用程序 特有的業務需求 主動拋出異常

示例

提示用戶 輸入密碼,如果 長度少於 8,拋出 異常

注意

當前函數 只負責 提示用戶輸入密碼,如果 密碼長度不正確,需要其他的函數進行額外處理因此可以 拋出異常,由其他需要處理的函數 捕獲異常

4.2 拋出異常

Python 中提供了一個 Exception 異常類在開發時,如果滿足 特定業務需求時,希望 拋出異常,可以: 創建 一個 Exception 的 對象 使用 raise 關鍵字 拋出 異常對象

需求

定義 input_password 函數,提示用戶輸入密碼如果用戶輸入長度 < 8,拋出異常如果用戶輸入長度 >=8,返回輸入的密碼

<code>def input_password():

# 1. 提示用戶輸入密碼
pwd = input("請輸入密碼:")

# 2. 判斷密碼長度,如果長度 >= 8,返回用戶輸入的密碼
if len(pwd) >= 8:
return pwd

# 3. 密碼長度不夠,需要拋出異常
# 1> 創建異常對象 - 使用異常的錯誤信息字符串作為參數
ex = Exception("密碼長度不夠")

# 2> 拋出異常對象
raise ex


try:
user_pwd = input_password()
print(user_pwd)
except Exception as result:
print("發現錯誤:%s" % result)/<code>

三、eval 函數

eval() 函數十分強大 —— 將字符串 當成 有效的表達式 來求值 並 返回計算結果

<code># 基本的數學計算
In [1]: eval("1 + 1")
Out[1]: 2

# 字符串重複
In [2]: eval("'*' * 10")
Out[2]: '**********'

# 將字符串轉換成列表


In [3]: type(eval("[1, 2, 3, 4, 5]"))
Out[3]: list

# 將字符串轉換成字典
In [4]: type(eval("{'name': 'xiaoming', 'age': 18}"))
Out[4]: dict/<code>

案例 - 計算器

需求

提示用戶輸入一個 加減乘除混合運算返回計算結果

<code>input_str = input("請輸入一個算術題:")

print(eval(input_str))/<code>

不要濫用 eval

在開發時千萬不要使用 eval 直接轉換 input 的結果

<code>__import__('os').system('ls')/<code>等價代碼

<code>import os

os.system("終端命令")/<code>執行成功,返回 0執行失敗,返回錯誤信息