Python元類應用之單例模式

1、什麼是Python元類

參考文章《Python元類

2、什麼是單例模式

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

如何保證一個類只有一個實例並且這個實例易於被訪問呢?定義一個全局變量可以確保對象隨時都可以被訪問,但不能防止我們實例化多個對象。一個更好的解決辦法是讓類自身負責保存它的唯一實例。這個類可以保證沒有其他實例被創建,並且它可以提供一個訪問該實例的方法。這就是單例模式的模式動機。

3、利用__new__實現單例

# -*- coding: utf8 -*-

class Singleton(object):
    def __init__(self):
        print 'entrance of __init__'

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

if __name__ == '__main__':
    s1 = Singleton()
    s2 = Singleton()

    print s1, s2

 Python中通常利用__new__函數實現單例模式。__new__函數負責構造對象,類似於C++中的構造函數。因此爲了使類只能創建一個實例對象,我們可以重載__new__函數的行爲,使其只能構造一個實例。在上述代碼中,給Singleton類賦予了一個_instance屬性,如果_instance屬性爲None則創建實例對象,並使_instance屬性引用(指向)該對象,否則直接返回_instance所引用的對象。因此代碼中的s1和s2實際上引用了同一個內存對象。

4、利用元類__call__實現單例

# -*- coding: utf8 -*-

# 單例模式
class SingletonMetaClass(type):
    def __init__(cls, *args, **kwargs):
        """
        初始化類
        :param args:
        :param kwargs:
        """
        print 'MetaClass.__init__ called'
        print cls._instance
        super(SingletonMetaClass, cls).__init__(*args, **kwargs)


    def __new__(cls, name, bases, attrs):
        """
        創建類,負責類的行爲和屬性的創建
        :param name:
        :param bases:
        :param attrs:
        :return:
        """
        print 'MetaClass.__new__ called'

        # 單例模式
        cls._instance = None

        return type.__new__(cls, name, bases, attrs)

    # __call__方法其實和類的創建過程和實例化沒有多大關係了,定義了__call__方法才能被使用函數的方式執行。
    # 被該元類創建的類,屬於該元類的一個實例。因此創建其實例的時候,會調用元類的__call_方法
    def __call__(cls, *args, **kwargs):
        """
        使類變爲可調用對象
        :param args:
        :param kwargs:
        :return:
        """
        print 'MetaClass.__call__ called'
        if cls._instance is None:
            # 這裏會去調用類的__new__函數
            cls._instance = super.__call__(SingletonMetaClass, cls).__call__(*args, **kwargs)
        return cls._instance

class A():
    __metaclass__ = SingletonMetaClass

    def __new__(cls, *args, **kwargs):
        print 'A.__new__ called'
        return super(A, cls).__new__(cls, *args, **kwargs)

if __name__ == '__main__':
    # 因爲類A是SingletonMetaClass的一個實例,執行A()時會調用元類SingletonMetaClass的__call__方法
    a1 = A()
    print a1
    a2 = A()
    print a2

 我們知道,在Python中類也是對象,元類是創建類的類,因此類實際上是元類的實例對象。在Python中,如果一個對象定義了__call__方法,那麼該對象爲可調用對象,意思是可以用調用函數的形式來調用對象。

Python的__new__方法負責創建對象,__init__方法負責初始化對象。在上述代碼中,只有類A被創建後才能創建類A的對象,因此爲了先創建出類A,SingletonMetaClass的__new__和__init__方法會首先被執行。當執行語句A()創建類A的對象時,根據__call__方法的定義,由於類A是元類SingletonMetaClass的對象,所以可以預料元類SingletonMetaClass的__call__方法會調用。

因此上述代碼中SiingletonMetaClass的__new__和__init__方法僅執行一次,而每次調用A()創建類A的實例時,都會調用SingletonMetaClass的__call__方法。因此爲了實現單例模式,我們在元類的__call__方法中判斷了cls的_instance屬性是否爲None,如果爲None則調用元類父類(即type)的__call__方法,type.__call__方法會調用類A的__new__方法創建類A的一個實例,然後將_instance屬性指向該實例,從而實現只有一個實例。下面是一次執行結果:

MetaClass.__new__ called
MetaClass.__init__ called
None
MetaClass.__call__ called
A.__new__ called
<__main__.A object at 0x00000000036D2EB8>
MetaClass.__call__ called
<__main__.A object at 0x00000000036D2EB8>

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