[python實現設計模式]-1. 單例模式


設計模式中,最簡單的一個就是 “單例模式”, 那么首先,就實現一下單例模式。

那么根據個人的理解,很快就寫出第一版。

# -*- coding: utf-8 -*-


class Singleton(object):

    # 定義靜態變量實例
    __singleton = None

    def __init__(self):
        pass

    @staticmethod
    def get_instance():
        if Singleton.__singleton is None:
            Singleton.__singleton = Singleton()
        return Singleton.__singleton

測試一下:

 

if __name__ == "__main__":
    instance1 = Singleton.get_instance()
    instance2 = Singleton.get_instance()

    print id(instance1)
    print id(instance2)



liutrumpdeMacBook-Air:singleton trump$ python Singleton
4419778640
4419778640

看起來運行良好。但是其實,這里面有2個問題.

 

1. 這里類方法getinstance()用於獲取單例,但是類本身也可以實例化,這樣的方式其實並不符合單例模式的要求。

 

if __name__ == "__main__":
    instance1 = Singleton.get_instance()
    instance2 = Singleton.get_instance()

    instance3 = Singleton()

    print id(instance1)
    print id(instance2)
    print id(instance3)

執行結果:
liutrumpdeMacBook-Air:singleton trump$ python Singleton 
4461824080
4461824080
4461824144

在c#或java的設計模式中,我們通常是通過私有化類的構造函數來殺死類本身的繁殖能力

然而python並沒有訪問限定強制約束, 那么怎么辦呢?

這個后續再說.

但是這樣做也有好處,代碼簡單,大家約定好這樣子調用就行了。

但是最好在類的命名上也體現了出來這是一個單例類.

 

2. 這個單例類並不是線程安全的.

比如我寫了如下的測試代碼來測試它的線程安全性.

def test_singleton_in_thread():
    print id(Singleton.get_instance())

if __name__ == "__main__":
    idx = 0
    while 1:
        MyThread(test_singleton_in_thread, []).start()
        idx += 1
        if idx > 0X100:
            break

很快,就發現這確實不是線程安全的....

 

 關於問題1. 我們換個思路, 來談一談python里面的構造函數.(其實python里面並沒有構造函數個概念,⊙﹏⊙, 叫習慣了而已)

 python 里的__init__(self) 函數,之前一直被我認為是python類的構造函數 __del__(self), 一直被我認為是類的析構函數...

其實,這是不對滴.

這時候__new__(self)就要登場了.

我們看一下官方的介紹.

http://docs.python.org/2/reference/datamodel.html#object.new

 

這么鬼長,其實告訴我們了一個道理.

new是一個類方法,會創建對象時調用。而init方法是在創建完對象后調用,對當前對象的實例做一些一些初始化,無返回值。如果重寫了new而在new里面沒有調用init或者沒有返回實例,那么init將不起作用。

我擦,類方法,又是什么鬼....

好吧,繼續查下資料.

http://www.cnblogs.com/2gua/archive/2012/09/03/2668125.html

靜態方法:無法訪問類屬性、實例屬性,相當於一個相對獨立的方法,跟類其實沒什么關系,換個角度來講,其實就是放在一個類的作用域里的函數而已。

類成員方法:可以訪問類屬性,無法訪問實例屬性。
類方法有類變量cls傳入,從而可以用cls做一些相關的處理。並且有子類繼承時,調用該類方法時,傳入的類變量cls是子類,而非父類。 
 
了解了這些姿勢以后,我們可以嘗試通過改造類的__new__方法來給類進行計划生育了.
 

嘗試了寫了一下.

然而報一個遞歸溢出......

 

查了一下. 借鑒了一下

http://stackoverflow.com/questions/31875/is-there-a-simple-elegant-way-to-define-singletons-in-python/31887#31887

# -*- coding: utf-8 -*-


class Singleton(object):

    # 定義靜態變量實例
    __instance = None

    def __init__(self):
        pass

    def __new__(cls, *args, **kwargs):
        if not cls.__instance:
            cls.__instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
        return cls.__instance

if __name__ == "__main__":
    instance1 = Singleton()
    instance2 = Singleton()

    print id(instance1)
    print id(instance2)

liutrumpdeMacBook-Air:singleton trump$ python Singleton3.py
4544985488
4544985488

 

這里不是很懂super的用法. 查了一下文檔.

 

 看起來是調用了object類的 __new__方法來構造出了我們需要的類.(類似於c#里的反射???,不知道python的解釋器是如何實現的)

 

總之看起來是靠譜的。耶耶耶。

同樣的, 這種寫法依然不是線程安全的.

 

關於問題2.

 

為了保證在多線程下線程安全性。 

我們在寫單例模式時候, 通常使用雙重檢查鎖定來檢測實例是否存在。

為什么用double check, 請自行思考...

實現: 

# -*- coding: utf-8 -*-
from MyThread import *
import threading

Lock = threading.Lock()


class Singleton(object):

    # 定義靜態變量實例
    __instance = None

    def __init__(self):
        pass

    def __new__(cls, *args, **kwargs):
        if not cls.__instance:
            try:
                Lock.acquire()
                # double check
                if not cls.__instance:
                    cls.__instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
            finally:
                Lock.release()
        return cls.__instance


def test_singleton_in_thread():
    print id(Singleton())

if __name__ == "__main__":
    idx = 0
    while 1:
        MyThread(test_singleton_in_thread, []).start()
        idx += 1
        if idx > 0X100:
            break

 

運行結果: 

證明是線程安全的. 歐耶.

 

上面的代碼在單例模式中被稱作,懶漢式單例.

還有一種稱之為餓漢式單例.

遺憾的是,python下是沒有辦法實現的。

餓漢,太餓了, 一開始就把實例構造出來...是不是很形象.

 

貼個c#的代碼,供參考.

解釋: 聲明一個私有static 成員實例並直接調用類默認靜態構造函數實例化.

然后安插共有靜態方法返回該實例.

使用CLR的靜態成員只能在靜態構造函數中構造並且只能構造一次的特性來實現了單例.

特別的直觀和美觀, 也保證了線程安全.

使用起來很方便.

但也有個弊端就是需要在類加載的時候就把實例給初始化出來.

當這個實例非常大或者構造很耗時的話此時的性能就會有影響.

 通常情況,這種寫法是使用最多的寫法(反正我寫的話肯定不想使用double check...)

class Singleton   
{   
    private static Singleton instance = new EagerSingleton();   
  
    private Singleton() { }   
  
    public static Singleton GetInstance()   
    {  
        return instance;   
    }  
} 

 

 

 

總結: 

本文介紹了使用python來實現不能約束構造實例只能通過規約指定方法來實現的單例模式,並由此引申控制類的__new__函數來

約束類構造的實例。

本文並且討論了多線程下的double check 的單例模式的實現。

本文並且討論了單例模式的懶漢式實現以及餓漢式實現.

 

 

好,第一個最簡單的設計模式的python實現就到這里。

to be continued.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM