python單例模式的實現

單例模式看wikipedia的解釋:

單例模式,也叫單子模式,是一種常用的軟件設計模式。在應用這個模式時,單例對象的類必須保證只有一個實例存在。

許多時候整個系統只需要擁有一個的全局對象,這樣有利於我們協調系統整體的行爲。比如在某個服務器程序中,該服務器的配置信息存放在一個文件中,這些配置數據由一個單例對象統一讀取,然後服務進程中的其他對象再通過這個單例對象獲取這些配置信息。這種方式簡化了在複雜環境下的配置管理。

實現單例模式的思路是:

  • 一個類能返回對象一個引用(永遠是同一個)和一個獲得該實例的方法(必須是靜態方法,通常使用getInstance這個名稱)。

  • 當我們調用這個方法時,如果類持有的引用不爲空就返回這個引用,如果類保持的引用爲空就創建該類的實例並將實例的引用賦予該類保持的引用。

  • 同時我們還將該類的構造函數定義爲私有方法,這樣其他處的代碼就無法通過調用該類的構造函數來實例化該類的對象,只有通過該類提供的靜態方法來得到該類的唯一實例。

首先,由於python不強制隱私,訪問權限不像Java那樣嚴格執行,所以它沒有私有構造函數這一說,全憑你自覺不要訪問敏感信息。其次,在python中最接近的構造函數是__new__(),但這很少使用。通常使用__init__(self),由它修改剛剛創建的對象(self代表實例本身)。最後,python既支持函數式編程又支持面向對象編程。

下面介紹兩種多線程安全的單例模式python實現。

1. 懶漢式(雙重校驗鎖)

# test.py
import threading
import time


class LazySingleton(object):
    # 類變量,__的寫法代表這是一個私有變量
    # 創建一個線程鎖
    __lock = threading.Lock()

    def __init__(self):
        print('初始化了一個類對象')
        # 實現阻塞5s中
        time.sleep(5)

    # 聲明一個類方法,相當與java中靜態(類)方法
    @classmethod
    def get_instance(cls, *args, **kwargs):
        try:
            # 訪問類變量__instance,如果不存在會按報錯處理,存在就返回
            LazySingleton.__instance
            return LazySingleton.__instance
        except:
            # 在執行with塊代碼的時候加上線程鎖,執行完畢釋放線程鎖
            # 此線程鎖不是GIL鎖
            with LazySingleton.__lock:
                try:
                    # 訪問類變量__instance,如果不存在會按報錯處理,存在就返回
                    LazySingleton.__instance
                    return LazySingleton.__instance
                except:
                    # 給LazySingleton類綁定一個變量__instance,相當與Java中的靜態(類變變量)
                    # __的寫法代表這是一個私有變量,雖然是私有變量,但是你也可以在類外部訪問,原則上你不要訪問
                    LazySingleton.__instance = LazySingleton()
                    return LazySingleton.__instance


if __name__ == "__main__":
    def task():
        # 獲取LazySingleton類實例/對象
        obj = LazySingleton.get_instance()
        print(obj)


    # 循環創建多個線程
    for i in range(10):
        t = threading.Thread(target=task)
        t.start()

提交執行:

ssh://[email protected]:22/opt/appl/anaconda3/bin/python -u /opt/appl/pycharm-projects/spark_streaming_test/test.py
初始化了一個類對象
<__main__.LazySingleton object at 0x7f13104469e8>
<__main__.LazySingleton object at 0x7f13104469e8>
<__main__.LazySingleton object at 0x7f13104469e8>
<__main__.LazySingleton object at 0x7f13104469e8>
<__main__.LazySingleton object at 0x7f13104469e8>
<__main__.LazySingleton object at 0x7f13104469e8>
<__main__.LazySingleton object at 0x7f13104469e8>
<__main__.LazySingleton object at 0x7f13104469e8>
<__main__.LazySingleton object at 0x7f13104469e8>
<__main__.LazySingleton object at 0x7f13104469e8>

Process finished with exit code 0

2. 裝飾器
如果不懂python的函數式編程建議把廖雪峯老師python 函數式編程 這一章看完,不然下面的代碼理解會有困難。

python用裝飾器實現單例模式思路有些改變了,要單例的類不用做任何改變,不用寫獲得該實例的方法,和正常類無區別。

# test2.py
import threading
from functools import wraps
import time


class Decorator(object):
    # 創建一個線程鎖,這是一個類變量,類似於Java中靜態變量
    __lock = threading.Lock()
    __instance = None

    # 定義一個高階函數,cls參數是一個類
    # 同時它是一個靜態方法,類似於Java中靜態(類)方法
    @staticmethod
    def singleton(cls):
        # 定義一個私有方法,wraps作用不知道的自己查,不感興趣的也不用知道
        @wraps(cls)
        def __wrapper(*args, **kw):
            # 如果類變量__instance不存在就新建,存在就返回
            if Decorator.__instance is None:
                # 在執行with塊代碼的時候加上線程鎖,執行完畢釋放線程鎖
                # 此線程鎖不是GIL鎖
                with Decorator.__lock:
                    # 如果類變量__instance不存在就新建,存在就返回
                    if Decorator.__instance is None:
                        # 新建一個類
                        Decorator.__instance = cls(*args, **kw)
                    return Decorator.__instance
            return Decorator.__instance

        # 返回值爲函數叫做閉包
        return __wrapper


# singleton函數是一個裝飾器,它接受一個類或函數作爲變量,並返回一個函數
# 創建類:DecoratorSingleton()相當於執行了singleton(DecoratorSingleton())
# lock是一個線程鎖
@Decorator.singleton
class DecoratorSingleton(object):
    # 初始化方法
    def __init__(self, name):
        print('my name is ' + name)
        # 實現阻塞5s
        time.sleep(5)


if __name__ == "__main__":
    # 創建一個任務函數
    def task():
        # 獲取DecoratorSingleton類實例/對象
        obj = DecoratorSingleton('dong')
        print(obj)


    # 循環創建多個線程
    for i in range(10):
        t = threading.Thread(target=task)
        t.start()

提交執行:

ssh://[email protected]:22/opt/appl/anaconda3/bin/python -u /opt/appl/pycharm-projects/spark_streaming_test/test2.py
my name is dong
<__main__.DecoratorSingleton object at 0x7f4dcd5329e8>
<__main__.DecoratorSingleton object at 0x7f4dcd5329e8>
<__main__.DecoratorSingleton object at 0x7f4dcd5329e8>
<__main__.DecoratorSingleton object at 0x7f4dcd5329e8>
<__main__.DecoratorSingleton object at 0x7f4dcd5329e8>
<__main__.DecoratorSingleton object at 0x7f4dcd5329e8>
<__main__.DecoratorSingleton object at 0x7f4dcd5329e8>
<__main__.DecoratorSingleton object at 0x7f4dcd5329e8>
<__main__.DecoratorSingleton object at 0x7f4dcd5329e8>
<__main__.DecoratorSingleton object at 0x7f4dcd5329e8>

Process finished with exit code 0

利用裝飾器實現的單例模式極其靈活,不僅可以用在類上面,還可以用在方法上面,對方法產生的結果進行單例子化。

Python還可以利用重寫__new__()實現單例,有興趣的同學可以自行研究。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章