1. 程式人生 > >python之單例設計模式、異常

python之單例設計模式、異常

一、單例設計模式

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__方法改造之後,每次都會得到一次被建立物件的引用,初始化方法會被再次呼叫

現在的需求是:讓初始化方法只執行一次

解決辦法:

  1. 義一個類屬性init_flag標記是否執行過初始化動作,初始值為False
  2. 在__init__方法中,判斷init_flag,如果False就會執行初始化動作
  3. 然後將init_flag設定為True
  4. 這樣,再次自動呼叫__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)