前言
單例模式(Singleton Pattern),是一種軟件設(shè)計模式,是類只能實例化一個對象,
目的是便于外界的訪問,節(jié)約系統(tǒng)資源,如果希望系統(tǒng)中 只有一個對象可以訪問,就用單例模式,
顯然單例模式的要點有三個;一是某個類只能有一個實例;二是它必須自行創(chuàng)建這個實例;三是它必須自行向整個系統(tǒng)提供這個實例。
在 Python 中,我們可以用多種方法來實現(xiàn)單例模式:
- 使用模塊
- 使用 __new__
- 使用裝飾器(decorator)
- 使用元類(metaclass)
概念
簡單說,單例模式(也叫單件模式)的作用就是保證在整個應(yīng)用程序的生命周期中,任何一個時刻,單例類的實例都只存在一個(當(dāng)然也可以不存在)
例子:
一臺計算機上可以連好幾個打印機,但是這個計算機上的打印程序只能有一個,這里就可以通過單例模式來避免兩個打印作業(yè)同時輸出到打印機中,即在整個的打印過程中我只有一個打印程序的實例。
super(B, self).__init__()是這樣理解的:super(B, self)首先找到B的父類(就是類A),然后把類B的對象self轉(zhuǎn)換為類A的對象(通過某種方式,一直沒有考究是什么方式,慚愧),然后“被轉(zhuǎn)換”的類A對象調(diào)用自己的__init__函數(shù)。考慮到super中只有指明子類的機制,因此,在多繼承的類定義中,通常我們保留使用類似代碼段1的方法。
1. super并不是一個函數(shù),是一個類名,形如super(B, self)事實上調(diào)用了super類的初始化函數(shù),
產(chǎn)生了一個super對象;
2. super類的初始化函數(shù)并沒有做什么特殊的操作,只是簡單記錄了類類型和具體實例;
3. super(B, self).func的調(diào)用并不是用于調(diào)用當(dāng)前類的父類的func函數(shù);
4. Python的多繼承類是通過mro的方式來保證各個父類的函數(shù)被逐一調(diào)用,而且保證每個父類函數(shù)
只調(diào)用一次(如果每個類都使用super);
5. 混用super類和非綁定的函數(shù)是一個危險行為,這可能導(dǎo)致應(yīng)該調(diào)用的父類函數(shù)沒有調(diào)用或者一
個父類函數(shù)被調(diào)用多次。
__new__: 對象的創(chuàng)建,是一個靜態(tài)方法,第一個參數(shù)是cls。(想想也是,不可能是self,對象還沒創(chuàng)建,哪來的self)
__init__ : 對象的初始化, 是一個實例方法,第一個參數(shù)是self。
__new__方法在類定義中不是必須寫的,如果沒定義,默認(rèn)會調(diào)用object.__new__去創(chuàng)建一個對象。如果定義了,就是override,可以custom創(chuàng)建對象的行為。
聰明的讀者可能想到,既然__new__可以custom對象的創(chuàng)建,那我在這里做一下手腳,每次創(chuàng)建對象都返回同一個,那不就是單例模式了嗎?沒錯,就是這樣。可以觀摩《飄逸的python - 單例模式亂彈》
定義單例模式時,因為自定義的__new__重載了父類的__new__,所以要自己顯式調(diào)用父類的__new__,即object.__new__(cls, *args, **kwargs),或者用super()。,不然就不是extend原來的實例了,而是替換原來的實例。
代碼
import threading
class Signleton(object):
def __init__(self):
print("__init__ method called")
def __new__(cls):
print("__new__ method called")
mutex=threading.Lock()
mutex.acquire() # 上鎖,防止多線程下出問題
if not hasattr(cls, 'instance'):
cls.instance = super(LogSignleton, cls).__new__(cls)
mutex.release()
return cls.instance
if __name__ == '__main__':
obj = Signleton()
輸出結(jié)果:
>>> ================================ RESTART ================================
>>>
__new__ method called
__init__ method called
>>>
說明
1.從輸出結(jié)果來看,最先調(diào)用 __new__ 方法,然后調(diào)用__init__方法
2. __new__ 通常用于控制生成一個新實例的過程,它是類級別的方法。
3. __init__ 通常用于初始化一個新實例,控制這個初始化的過程,比如添加一些屬性,做一些額外的操作,發(fā)生在類實例被創(chuàng)建完以后。它是實例級別的方法。
方法1
__new__ 在__init__初始化前,就已經(jīng)實例化對象,可以利用這個方法實現(xiàn)單例模式。
print '----------------------方法1--------------------------'
#方法1,實現(xiàn)__new__方法
#并在將一個類的實例綁定到類變量_instance上,
#如果cls._instance為None說明該類還沒有實例化過,實例化該類,并返回
#如果cls._instance不為None,直接返回cls._instance
class Singleton(object):
def __new__(cls, *args, **kw):
if not hasattr(cls, '_instance'):
orig = super(Singleton, cls)
cls._instance = orig.__new__(cls, *args, **kw)
return cls._instance
class MyClass(Singleton):
a = 1
one = MyClass()
two = MyClass()
two.a = 3
print one.a
#3
#one和two完全相同,可以用id(), ==, is檢測
print id(one)
#29097904
print id(two)
#29097904
print one == two
#True
print one is two
#True
方法2
print '----------------------方法2--------------------------'
#方法2,共享屬性;所謂單例就是所有引用(實例、對象)擁有相同的狀態(tài)(屬性)和行為(方法)
#同一個類的所有實例天然擁有相同的行為(方法),
#只需要保證同一個類的所有實例具有相同的狀態(tài)(屬性)即可
#所有實例共享屬性的最簡單最直接的方法就是__dict__屬性指向(引用)同一個字典(dict)
#可參看:http://code.activestate.com/recipes/66531/
class Borg(object):
_state = {}
def __new__(cls, *args, **kw):
ob = super(Borg, cls).__new__(cls, *args, **kw)
ob.__dict__ = cls._state
return ob
class MyClass2(Borg):
a = 1
one = MyClass2()
two = MyClass2()
#one和two是兩個不同的對象,id, ==, is對比結(jié)果可看出
two.a = 3
print one.a
#3
print id(one)
#28873680
print id(two)
#28873712
print one == two
#False
print one is two
#False
#但是one和two具有相同的(同一個__dict__屬性),見:
print id(one.__dict__)
#30104000
print id(two.__dict__)
#30104000
方法3
print '----------------------方法3--------------------------'
#方法3:本質(zhì)上是方法1的升級(或者說高級)版
#使用__metaclass__(元類)的高級python用法
class Singleton2(type):
def __init__(cls, name, bases, dict):
super(Singleton2, cls).__init__(name, bases, dict)
cls._instance = None
def __call__(cls, *args, **kw):
if cls._instance is None:
cls._instance = super(Singleton2, cls).__call__(*args, **kw)
return cls._instance
class MyClass3(object):
__metaclass__ = Singleton2
one = MyClass3()
two = MyClass3()
two.a = 3
print one.a
#3
print id(one)
#31495472
print id(two)
#31495472
print one == two
#True
print one is two
#True
方法4
print '----------------------方法4--------------------------'
#方法4:也是方法1的升級(高級)版本,
#使用裝飾器(decorator),
#這是一種更pythonic,更elegant的方法,
#單例類本身根本不知道自己是單例的,因為他本身(自己的代碼)并不是單例的
def singleton(cls, *args, **kw):
instances = {}
def _singleton():
if cls not in instances:
instances[cls] = cls(*args, **kw)
return instances[cls]
return _singleton
@singleton
class MyClass4(object):
a = 1
def __init__(self, x=0):
self.x = x
one = MyClass4()
two = MyClass4()
two.a = 3
print one.a
#3
print id(one)
#29660784
print id(two)
#29660784
print one == two
#True
print one is two
#True
one.x = 1
print one.x
#1
print two.x
單例模式
單例模式(Singleton Pattern)是一種常用的軟件設(shè)計模式,該模式的主要目的是確保某一個類只有一個實例存在。當(dāng)你希望在整個系統(tǒng)中,某個類只能出現(xiàn)一個實例時,單例對象就能派上用場。
比如,某個服務(wù)器程序的配置信息存放在一個文件中,客戶端通過一個 AppConfig 的類來讀取配置文件的信息。如果在程序運行期間,有很多地方都需要使用配置文件的內(nèi)容,也就是說,很多地方都需要創(chuàng)建 AppConfig 對象的實例,這就導(dǎo)致系統(tǒng)中存在多個 AppConfig 的實例對象,而這樣會嚴(yán)重浪費內(nèi)存資源,尤其是在配置文件內(nèi)容很多的情況下。事實上,類似 AppConfig 這樣的類,我們希望在程序運行期間只存在一個實例對象。
在 Python 中,我們可以用多種方法來實現(xiàn)單例模式:
- 使用模塊
- 使用 __new__
- 使用裝飾器(decorator)
- 使用元類(metaclass)
使用模塊
其實,Python 的模塊就是天然的單例模式,因為模塊在第一次導(dǎo)入時,會生成 .pyc 文件,當(dāng)?shù)诙螌?dǎo)入時,就會直接加載 .pyc 文件,而不會再次執(zhí)行模塊代碼。因此,我們只需把相關(guān)的函數(shù)和數(shù)據(jù)定義在一個模塊中,就可以獲得一個單例對象了。如果我們真的想要一個單例類,可以考慮這樣做:
# mysingleton.py
class My_Singleton(object):
def foo(self):
pass
my_singleton = My_Singleton()
將上面的代碼保存在文件 mysingleton.py 中,然后這樣使用:
from mysingleton import my_singleton
my_singleton.foo()
使用 __new__
為了使類只能出現(xiàn)一個實例,我們可以使用 __new__ 來控制實例的創(chuàng)建過程,代碼如下:
class Singleton(object):
_instance = None
def __new__(cls, *args, **kw):
if not cls._instance:
cls._instance = super(Singleton, cls).__new__(cls, *args, **kw)
return cls._instance
class MyClass(Singleton):
a = 1
在上面的代碼中,我們將類的實例和一個類變量 _instance 關(guān)聯(lián)起來,如果 cls._instance 為 None 則創(chuàng)建實例,否則直接返回 cls._instance。
執(zhí)行情況如下:
>>> one = MyClass()
>>> two = MyClass()
>>> one == two
True
>>> one is two
True
>>> id(one), id(two)
(4303862608, 4303862608)
使用裝飾器
我們知道,裝飾器(decorator)可以動態(tài)地修改一個類或函數(shù)的功能。這里,我們也可以使用裝飾器來裝飾某個類,使其只能生成一個實例,代碼如下:
from functools import wraps
def singleton(cls):
instances = {}
@wraps(cls)
def getinstance(*args, **kw):
if cls not in instances:
instances[cls] = cls(*args, **kw)
return instances[cls]
return getinstance
@singleton
class MyClass(object):
a = 1
在上面,我們定義了一個裝飾器 singleton,它返回了一個內(nèi)部函數(shù) getinstance,該函數(shù)會判斷某個類是否在字典 instances 中,如果不存在,則會將 cls 作為 key,cls(*args, **kw) 作為 value 存到 instances 中,否則,直接返回 instances[cls]。
使用 metaclass
元類(metaclass)可以控制類的創(chuàng)建過程,它主要做三件事:
- 攔截類的創(chuàng)建
- 修改類的定義
- 返回修改后的類
使用元類實現(xiàn)單例模式的代碼如下:
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
# Python2
class MyClass(object):
__metaclass__ = Singleton
# Python3
# class MyClass(metaclass=Singleton):
# pass
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

