Python中的單例設計模組及異常
阿新 • • 發佈:2018-12-10
一、單例設計模式
1、設計模式
設計模式是前人的總結和經驗的提煉,通常被人們廣為流傳
2、單例
由類建立的物件,在系統中有唯一的例項
3、建立物件的步驟
(1)為物件分配空間:__new__()
建立物件的時候,python直譯器首先會呼叫__new__方法為物件分配空間,__new__是一個有object基類提供的內建的靜態方法,主要有兩個作用:
- 在記憶體中為物件分配空間
- 返回物件的引用
(2)物件初始化:__init__()
Python直譯器獲得物件的引用後,將引用的第一個引數,傳遞給__init__方法
4、例項分析
class MusicPlayer(object): def __new__(cls, *args, **kwargs): #第一個引數cls:哪一個類呼叫就傳遞哪一個類 #第二個引數*args:是一個多值元組引數 #第三個引數**kwargs:是一個多值字典引數 #1.建立物件時,new方法會被自動呼叫 print '建立物件,分配空間'#重寫了父類 #2.為物件分配空間 #__new__方法是一個靜態方法,在呼叫時,第一個引數是cls instance = object.__new__(cls) #3.返回物件的引用 return instance def __init__(self): print '初始化播放器' #建立播放器物件 player1 =MusicPlayer() print player1 player2 =MusicPlayer() print player2
二、單例的高階用法
1、重寫__new__方法:繼承自父親類方法,返回父親類方法是呼叫__new__方法的結果
應該注意的是重寫__new__方法一定要return object.__new__(cls):
否則Python的直譯器得不到分配空間的物件的引用,就不會呼叫物件的初始化方法
- 定義一個類屬性,初始值為None,用於記錄單例物件的引用(當一個類定義完成執行程式的時候,記憶體中由這個類建立的物件嗎?) 並沒有只有我們需要呼叫建立物件的方法,記憶體中才會有第一個物件
- 重寫__new__方法
- 如果類屬性is None,呼叫父類方法分配空間,並在類屬性中記錄結果
- 返回類屬性中記錄的物件的引用
例項:
class MusicPlayer(object): instance=None def __new__(cls, *args, **kwargs): #第一個引數cls:哪一個類呼叫就傳遞哪一個類 #第二個引數*args:是一個多值元組引數 #第三個引數**kwargs:是一個多值字典引數 # 判斷類屬性是否為空(如果是空物件,說明第一個物件還沒被建立) if cls.instance is None: # 呼叫父類的方法,為第一個物件分配空間 cls.instance=object.__new__(cls) #返回類屬性儲存的物件引用 return cls.instance player1 =MusicPlayer() print player1 player2 =MusicPlayer() print player2
2、呼叫一次內建方法
在每次使用 類名() 建立物件的時候,Python的直譯器都會自動呼叫兩個方法
- __new__ 分配空間
- __init__ 物件初始化
在之前例項中,__new__方法改造之後,每次都會得到一次被建立物件的引用,初始化方法會被再次呼叫
現在的需求是:讓初始化方法只執行一次
解決辦法:
- 定義一個類屬性init_flag標記是否執行過初始化動作,初始值為False
- 在__init__方法中,判斷init_flag,如果False就會執行初始化動作
- 然後將init_flag設定為True
- 這樣,再次自動呼叫__init__方法時,初始化動作就不會再次被執行了
例項:
class MusicPlayer(object):
# 記錄第一個被建立物件的應用
instance = None
init_flag = False
def __new__(cls, *args, **kwargs):
# 判斷類屬性是否為空(如果是空物件,說明第一個物件還沒被建立)
if cls.instance is None:
# 呼叫父類的方法,為第一個物件分配空間
cls.instance = object.__new__(cls)
# 返回類屬性儲存的物件引用
return cls.instance
def __init__(self):
# 1.判斷是否執行過初始化方法
if MusicPlayer.init_flag:
return
# 2.如果沒有執行,執行初始化動作
print '初始化播放器'
# 3.修改類屬性的標記
MusicPlayer.init_flag = True
# 建立多個物件
player1 = MusicPlayer()
print player1
player2 = MusicPlayer()
print player2
三、異常
1、捕獲異常
(1)什麼時候會出現異常?
程式在執行的時候,如果python直譯器遇到一個錯誤,會停止程式的執行,並且提示一些錯誤的資訊,這就是異常
那麼在程式開發時,很難將所有的特殊情況都處理,通常異常捕獲可以針對突發事件做集中處理,從而保證程式的健壯性和穩定性
(2)如何捕捉異常?
在程式開發中,如果對某些程式碼的執行不能確定(程式語法完全正確),可以增加try來捕獲異常
try:
嘗試執行的程式碼
except:
出現錯誤的處理
2、異常已知的情況
根據錯誤型別來捕獲異常
執行步驟:
try:
嘗試執行的程式碼
except 錯誤型別1:
針對錯誤型別1,對應的程式碼處理
except 錯誤型別2:
針對錯誤型別2,對應的程式碼處理
...
需求:
- 提示使用者輸入一個整數
- 使用8除以使用者輸入的整數並輸出
try:
#提示使用者輸入一個整數
num = int(raw_input('輸入一個整數:'))
#使用8除以整數並輸出
result = 8/num
print result
except ZeroDivisionError:
print '0不能做除數'
except ValueError:
print '輸入的值不是數字'
print '*' *50
當你不清楚錯誤型別時 : except Exception as result:
try:
#提示使用者輸入一個整數
num = int(raw_input('輸入一個整數:'))
#使用8除以整數並輸出
result = 8/num
print result
# except ZeroDivisionError:
# print '0不能做除數'
except ValueError:
print '輸入的值不是數字'
except Exception as result:
print '未知錯誤%s'%result
finally:
#無論是否有異常,都會執行的程式碼
print '無論是否有異常,都會執行的程式碼'
3、主動丟擲異常
需求:提示使用者輸入密碼,如果長度小於8,就丟擲異常
def input_passwd():
#1.提示使用者數入密碼
pwd = raw_input('請輸入密碼:')
#2.判斷密碼的長度 >=8,返回使用者的密碼
if len(pwd)>=8:
return pwd
#3.如果<8主動丟擲異常
print '主動丟擲異常'
#1.建立異常物件
ex =Exception('密碼長度不夠')
#2.主動丟擲異常
raise ex
#注意:只丟擲異常而不捕獲異常,程式碼會出錯
try:
print input_passwd()
except Exception as result:
print result
4、異常的傳遞
當函式/方法的執行出現異常,會將異常傳遞給函式/方法呼叫的一方
def demo1():
return int(raw_input('請輸入整數:'))
def demo2():
return demo1()
#函式的錯誤:一級一級的去找,最終會將異常傳遞到主程式裡
#print demo2()
try:
print demo2()
except Exception as result:
print '未知錯誤%s'%result
5、斷言
可以理解為提前預言,讓人更好的知道錯誤原因
def func(num,div):
assert (div!=0),'div不能為0'
return num/div
print func(10,0)