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

設計模式中,最簡單的一個就是 「單例模式」, 那麼首先,就實現一下單例模式。html

那麼根據我的的理解,很快就寫出初版。java

# -*- 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

測試一下:python

 

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

    print id(instance1)
    print id(instance2)



liutrumpdeMacBook-Air:singleton trump$ python Singleton
4419778640
4419778640c#

看起來運行良好。可是其實,這裏面有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.

相關文章
相關標籤/搜索