1. 程式人生 > >python中的單例模式 python中的單例模式

python中的單例模式 python中的單例模式

python中的單例模式

 

一、單例模式的概述:

單例模式是一種常用的軟體設計模式。在它的核心結構中只包含一個被稱為單例類的特殊類。通過單例模式可以保證系統中一個類只有一個例項而且該例項易於外界訪問,從而方便對例項個數的控制並節約系統資源。如果希望在系統中某個類的物件只能存在一個,單例模式是最好的解決方案。

單例模式的要點有三個;一是某個類只能有一個例項;二是它必須自行建立這個例項;三是它必須自行向整個系統提供這個例項。

從具體實現角度來說,就是以下三點:一是單例模式的類只提供私有的建構函式,二是類定義中含有一個該類的靜態私有物件,三是該類提供了一個靜態的共有函式用於建立或獲取它本身的靜態私有物件。

 

二、應用:

一些資源管理器常常設計成單例模式

在計算機系統中,需要管理的資源包括軟體外部資源,譬如每臺計算機可以有若干個印表機,但只能有一個Printer Spooler,以避免兩個印表機作業同時輸出到印表機中。每臺計算機可以有若干傳真機卡,但是隻應該有一個軟體負責管理傳真卡,以避免一個通訊埠同時被兩個請求同時呼叫。

需要管理的資源包括軟體內部資源,譬如,大多數的軟體都有一個(甚至多個)屬性(properties)檔案存放系統配置。這樣的系統應當由一個物件來管理一個屬性檔案。

需要管理的軟體內部資源也包括負責記錄網站來訪人數的部件,記錄軟體系統內部事件、出錯資訊的部件,或是對系統的表現進行檢查的部件等。其一,這些資源管理器構件必須只有一個例項;其二,它們必須自行初始化;其三,允許整個系統訪問自己。因此,它們都滿足單例模式的條件,是單例模式的應用。

 

三、單例模式的優缺點:

優點:

1、例項控制

單例模式會阻止其他物件例項化其自己的單例物件的副本,從而確保所有物件都訪問唯一例項。

2、靈活性

因為類控制了例項化過程,所以類可以靈活更改例項化過程。

缺點:

1、開銷

雖然數量很少,但如果每次物件請求引用時都要檢查是否存在類的例項,將仍然需要一些開銷。可以通過使用靜態初始化解決此問題。

2、可能的開發混淆

使用單例物件(尤其在類庫中定義的物件)時,開發人員必須記住自己不能使用new關鍵字例項化物件。因為可能無法訪問庫原始碼,因此應用程式開發人員可能會意外發現自己無法直接例項化此類。

3、物件生成期

不能解決刪除單個物件的問題。在提供記憶體管理的語言中(例如基於.NET Framework的語言),只有單例類能夠導致單例類中出現懸浮引用。

 

四、在Python中,單例模式有以下幾種實現方式。

方法一、實現__new__方法,然後將類的一個例項繫結到類變數_instance上;如果cls._instance為None,則說明該類還沒有被例項化過,new一個該類的例項,並返回;如果cls._instance不為None,直接返回_instance,程式碼如下:

複製程式碼
class Singleton(object):
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls,'_instance'):
            orig=super(Singleton,cls)
            cls._instance=orig.__new__(cls,*args,**kwargs)
        return cls._instance

class MyClass(Singleton):
    a=1

one=MyClass()
two=MyClass()

# one和two完全相同,可以用id(),==,is檢查
print(one.a)    # 1
print(id(one))  # 2565285375728
print(id(two))  # 2565285375728
print(one == two)   # True
print(one is two)   # True
複製程式碼

方法二、本質上是方法一的升級版,使用metaclass(元類)的python高階用法,具體程式碼如下

複製程式碼
"""
class Singleton中的__init__在Myclass宣告的時候被執行Myclass=Singleton()
Myclass()執行時,最先執行父類的__call__方法(object,Singleton都作為Myclass的父類,
根據深度優先演算法,會執行Singleton中的__call__(),Singleton中的__call__()寫了單例模式)
"""
class Singleton(type):

    def __init__(self, name, bases, dict):
        super(Singleton,self).__init__(name,bases, dict)
        self._instance = None

    def __call__(self, *args, **kwargs):
        if self._instance is None:
            self._instance = super(Singleton,self).__call__(*args, **kwargs)
        return self._instance

class MyClass(object,metaclass=Singleton):
    a = 1

one=MyClass()
two=MyClass()
print(id(one))  # 1553247294800
print(id(two))  # 1553247294800
print(one == two)   # True
print(one is two)   # True
複製程式碼

 

方法三、使用python的裝飾器(decorator)實現單例模式,這是一種更Pythonic的方法;單例類本身的程式碼不是單例的,通過裝飾器使其單例化,程式碼如下:

複製程式碼
def singleton(cls, *args, **kwargs):
    instances = {}
    
    def _singleton():
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return _singleton

@singleton
class MyClass3(object):
    a = 1

one = MyClass3()
two = MyClass3()

print(id(one))  # 2880466769232
print(id(two))  # 2880466769232
print(one == two)   # True
print(one is two)   # True
複製程式碼

python的單例模式__new__()在__init__()之前被呼叫,用於生產例項物件。利用這個方法和類的屬性的特點可以實現設計模式的單例模式。單例模式是指建立唯一物件,單例模式設計的類只能例項化一個物件。

複製程式碼
class Singleton(object):
    __instance=None

    def __init__(self):
        pass
    def __new__(cls, *args, **kwargs):
        if Singleton.__instance is None:
            Singleton.__instance=object.__new__(cls,*args, **kwargs)
        return Singleton.__instance

one=Singleton()
two=Singleton()
print(id(one))  # 2488569943376
print(id(two))  # 2488569943376
print(one == two)   # True
print(one is two)   # True
複製程式碼

單例模式是一種常用的軟體設計模式。在它的核心結構中只包含一個被稱為單列類的特殊類。通過單列模式可以保證系統中一個類只有一個例項而且該例項易於外界訪問,從而便於對例項個數的控制並節約系統資源。如果希望在系統中某個類的物件只能存在一個,單例模式是最後的解決方案。

 

五、總結

內容:保證一個類只有一個例項,並提供一個訪問它的全域性訪問點。
適用場景:當類只能有一個例項而且客戶可以從一個眾所周知的訪問點訪問它時
優點:
    對唯一例項的受控訪問
    單例相當於全域性變數,但防止了名稱空間被汙染
與單例模式功能相似的概念:全域性變數、靜態變數(方法)

一、單例模式的概述:

單例模式是一種常用的軟體設計模式。在它的核心結構中只包含一個被稱為單例類的特殊類。通過單例模式可以保證系統中一個類只有一個例項而且該例項易於外界訪問,從而方便對例項個數的控制並節約系統資源。如果希望在系統中某個類的物件只能存在一個,單例模式是最好的解決方案。

單例模式的要點有三個;一是某個類只能有一個例項;二是它必須自行建立這個例項;三是它必須自行向整個系統提供這個例項。

從具體實現角度來說,就是以下三點:一是單例模式的類只提供私有的建構函式,二是類定義中含有一個該類的靜態私有物件,三是該類提供了一個靜態的共有函式用於建立或獲取它本身的靜態私有物件。

 

二、應用:

一些資源管理器常常設計成單例模式

在計算機系統中,需要管理的資源包括軟體外部資源,譬如每臺計算機可以有若干個印表機,但只能有一個Printer Spooler,以避免兩個印表機作業同時輸出到印表機中。每臺計算機可以有若干傳真機卡,但是隻應該有一個軟體負責管理傳真卡,以避免一個通訊埠同時被兩個請求同時呼叫。

需要管理的資源包括軟體內部資源,譬如,大多數的軟體都有一個(甚至多個)屬性(properties)檔案存放系統配置。這樣的系統應當由一個物件來管理一個屬性檔案。

需要管理的軟體內部資源也包括負責記錄網站來訪人數的部件,記錄軟體系統內部事件、出錯資訊的部件,或是對系統的表現進行檢查的部件等。其一,這些資源管理器構件必須只有一個例項;其二,它們必須自行初始化;其三,允許整個系統訪問自己。因此,它們都滿足單例模式的條件,是單例模式的應用。

 

三、單例模式的優缺點:

優點:

1、例項控制

單例模式會阻止其他物件例項化其自己的單例物件的副本,從而確保所有物件都訪問唯一例項。

2、靈活性

因為類控制了例項化過程,所以類可以靈活更改例項化過程。

缺點:

1、開銷

雖然數量很少,但如果每次物件請求引用時都要檢查是否存在類的例項,將仍然需要一些開銷。可以通過使用靜態初始化解決此問題。

2、可能的開發混淆

使用單例物件(尤其在類庫中定義的物件)時,開發人員必須記住自己不能使用new關鍵字例項化物件。因為可能無法訪問庫原始碼,因此應用程式開發人員可能會意外發現自己無法直接例項化此類。

3、物件生成期

不能解決刪除單個物件的問題。在提供記憶體管理的語言中(例如基於.NET Framework的語言),只有單例類能夠導致單例類中出現懸浮引用。

 

四、在Python中,單例模式有以下幾種實現方式。

方法一、實現__new__方法,然後將類的一個例項繫結到類變數_instance上;如果cls._instance為None,則說明該類還沒有被例項化過,new一個該類的例項,並返回;如果cls._instance不為None,直接返回_instance,程式碼如下:

複製程式碼
class Singleton(object):
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls,'_instance'):
            orig=super(Singleton,cls)
            cls._instance=orig.__new__(cls,*args,**kwargs)
        return cls._instance

class MyClass(Singleton):
    a=1

one=MyClass()
two=MyClass()

# one和two完全相同,可以用id(),==,is檢查
print(one.a)    # 1
print(id(one))  # 2565285375728
print(id(two))  # 2565285375728
print(one == two)   # True
print(one is two)   # True
複製程式碼

方法二、本質上是方法一的升級版,使用metaclass(元類)的python高階用法,具體程式碼如下

複製程式碼
"""
class Singleton中的__init__在Myclass宣告的時候被執行Myclass=Singleton()
Myclass()執行時,最先執行父類的__call__方法(object,Singleton都作為Myclass的父類,
根據深度優先演算法,會執行Singleton中的__call__(),Singleton中的__call__()寫了單例模式)
"""
class Singleton(type):

    def __init__(self, name, bases, dict):
        super(Singleton,self).__init__(name,bases, dict)
        self._instance = None

    def __call__(self, *args, **kwargs):
        if self._instance is None:
            self._instance = super(Singleton,self).__call__(*args, **kwargs)
        return self._instance

class MyClass(object,metaclass=Singleton):
    a = 1

one=MyClass()
two=MyClass()
print(id(one))  # 1553247294800
print(id(two))  # 1553247294800
print(one == two)   # True
print(one is two)   # True
複製程式碼

 

方法三、使用python的裝飾器(decorator)實現單例模式,這是一種更Pythonic的方法;單例類本身的程式碼不是單例的,通過裝飾器使其單例化,程式碼如下:

複製程式碼
def singleton(cls, *args, **kwargs):
    instances = {}
    
    def _singleton():
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return _singleton

@singleton
class MyClass3(object):
    a = 1

one = MyClass3()
two = MyClass3()

print(id(one))  # 2880466769232
print(id(two))  # 2880466769232
print(one == two)   # True
print(one is two)   # True
複製程式碼

python的單例模式__new__()在__init__()之前被呼叫,用於生產例項物件。利用這個方法和類的屬性的特點可以實現設計模式的單例模式。單例模式是指建立唯一物件,單例模式設計的類只能例項化一個物件。

複製程式碼
class Singleton(object):
    __instance=None

    def __init__(self):
        pass
    def __new__(cls, *args, **kwargs):
        if Singleton.__instance is None:
            Singleton.__instance=object.__new__(cls,*args, **kwargs)
        return Singleton.__instance

one=Singleton()
two=Singleton()
print(id(one))  # 2488569943376
print(id(two))  # 2488569943376
print(one == two)   # True
print(one is two)   # True
複製程式碼

單例模式是一種常用的軟體設計模式。在它的核心結構中只包含一個被稱為單列類的特殊類。通過單列模式可以保證系統中一個類只有一個例項而且該例項易於外界訪問,從而便於對例項個數的控制並節約系統資源。如果希望在系統中某個類的物件只能存在一個,單例模式是最後的解決方案。

 

五、總結

內容:保證一個類只有一個例項,並提供一個訪問它的全域性訪問點。
適用場景:當類只能有一個例項而且客戶可以從一個眾所周知的訪問點訪問它時
優點:
    對唯一例項的受控訪問
    單例相當於全域性變數,但防止了名稱空間被汙染
與單例模式功能相似的概念:全域性變數、靜態變數(方法)