1. 程式人生 > >python進階(10)——單例/異常

python進階(10)——單例/異常

單例

設計模式:前人留下的對某一特定問題的成熟的解決方案(套路)

單例設計模式:類建立的物件,在系統中只有唯一一個例項,每一次執行類名()返回的物件,記憶體地址是相同的

__new__方法:

使用類名()建立物件時,python直譯器執行兩步:

1首先呼叫__new__內建方法為物件分配空間,返回物件的引用(地址)

2呼叫__init__內建方法進行物件初始化

class MusicPlayer(object):
    pass


# 建立多個物件
player1 = MusicPlayer()
print(player1)

player2 = MusicPlayer()
print(player2)
<__main__.MusicPlayer object at 0x10126a4a8>
<__main__.MusicPlayer object at 0x10126a470>

輸出的兩個記憶體地址不一樣,說明player1和player2是兩個完全不同的物件

單例設計模式的目的就是無論建立多少次,物件的引用(地址)是相同的,怎麼解決?

定義一個類屬性instance,初始值是None,用於記錄物件的引用(初始時沒有值)

class MusicPlayer(object):

    # 定義類屬性,記錄第一個被建立物件的引用
    instance = None

    def __new__(cls, *args, **kwargs):

        # 1 判斷類屬性是否是空物件
        if cls.instance is None:
            # 2 呼叫父類的方法,為第一個物件分配空間
            cls.instance = super().__new__(cls)

        # 3 返回類屬性儲存的物件引用
        return cls.instance


# 建立多個物件
player1 = MusicPlayer()
print(player1)

player2 = MusicPlayer()
print(player2)
<__main__.MusicPlayer object at 0x1049c5438>
<__main__.MusicPlayer object at 0x1049c5438>

如果想要初始化動作只執行一次?

定義類屬性,判斷是否執行過初始化

class MusicPlayer(object):

    # 定義類屬性,記錄第一個被建立物件的引用
    instance = None

    # 定義類屬性,記錄是否執行過初始化動作
    init_flag = False

    def __new__(cls, *args, **kwargs):

        # 1 判斷類屬性是否是空物件
        if cls.instance is None:
            # 2 呼叫父類的方法,為第一個物件分配空間
            cls.instance = super().__new__(cls)

        # 3 返回類屬性儲存的物件引用
        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)

異常

程式在執行時,遇到錯誤停止執行並提示一些錯誤資訊

通過異常捕獲,保證程式的健壯性

在程式的開發中,如果對某些程式碼的執行不確定是否正確,可以增加try來捕獲異常

格式:

try:
嘗試執行的程式碼
except:
出現錯誤的處理
try:

    num = int(input("請輸入一個整數:"))
except:
    
    print("請輸入正確的整數")

print("-" * 50)
請輸入一個整數:eee
請輸入正確的整數
--------------------------------------------------

異常型別捕獲演練:

輸入一個非零整數,進行除以8的操作,

錯誤型別:

try:

    num = int(input("請輸入一個整數:"))
    result = 8 / num
    print(result)

except ZeroDivisionError:
    print("除0錯誤")

except ValueError:
    print("請輸入正確的整數")

以上捕獲的錯誤型別都是已知的錯誤,如果想對未知的錯誤進行捕獲?

可以再增加一個except

try:

    num = int(input("請輸入一個整數:"))
    result = 8 / num
    print(result)

except ZeroDivisionError:
    print("除0錯誤")

#假設輸入非整數時為未知錯誤
except Exception as result:
    print("未知錯誤%s" % result)
請輸入一個整數:a
未知錯誤invalid literal for int() with base 10: 'a'

異常捕獲完整語法

try:

    num = int(input("請輸入一個整數:"))
    result = 8 / num
    print(result)

except ZeroDivisionError:
    print("除0錯誤")

except ValueError:
        print("請輸入正確的整數")

except Exception as result:
    print("未知錯誤%s" % result)

else:
    print("嘗試成功")

finally:
    print("無論是否出現錯誤都會執行的程式碼")

異常的傳遞:

把異常傳遞給函式或方法的呼叫方,程式不會被立刻終止

例:定義函式demo1()提示使用者輸入一個整數並返回;定義函式demo2()呼叫demo1();在主程式中使用demo2()

def demo1():
    return int(input("輸入整數:"))


def demo2():
    return demo1()


# 利用異常的傳遞性,在主程式捕獲異常
try:
    print(demo2())
except Exception as result:
    print("位置錯誤 %s" % result)

執行結果

輸入整數:a
位置錯誤 invalid literal for int() with base 10: 'a'

注:

在開發中,在主函式中新增異常捕獲

在主函式中呼叫的其他函式,只要出現異常,都會傳遞到主函式的異常捕獲中

這樣就不需要在程式碼中,增加大量的異常捕獲,能夠保證程式碼的整潔性

 

在實際開發中,除了程式碼出錯需要丟擲異常,還可以根據應用程式特有的業務需求主動丟擲異常:

1.建立exception異常物件

2.使用raise關鍵字丟擲異常物件

例,提示使用者輸入密碼,如果長度少於8,丟擲異常

def input_password():

    # 1.輸入密碼
    pwd = input("請輸入密碼")

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

    # 3.如果<8主動丟擲異常
    print("主動丟擲異常")
    ex = Exception("密碼長度不夠")
    raise ex

try:
    print(input_password())
except Exception as result:
    print(result)