python中的單例可以利用__new__和__init__來實作。每次建立執行個體的時候總會獲得同一個執行個體,但是每次也會執行__init__方法。這就會造成單例中的屬性會被修改,更重要的是執行個體會被重新初始化。有時候我們并不希望再次初始化執行個體,我們希望直接獲得已經建立好的執行個體。應用類變量和鎖機制,可以實作需求。
一個簡單的單例
import threading
from concurrent.futures.thread import ThreadPoolExecutor
class Singleton:
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self, num):
self.num = num
self.__init_flag = True
def get_name(self):
return self.num
def run_job(num):
obj = Singleton(num)
print("Thread:%s ID:%s NUM:%s" % (num, id(obj), obj.num))
with ThreadPoolExecutor(max_workers=5) as pool:
for line in range(10):
pool.submit(run_job, line)
上面樣例的執行結果為。
Thread:0 ID:43341256 NUM:0
Thread:1 ID:43341256 NUM:1
Thread:2 ID:43341256 NUM:2
Thread:3 ID:43341256 NUM:3
Thread:4 ID:43341256 NUM:4
Thread:5 ID:43341256 NUM:5Thread:6 ID:43341256 NUM:6
Thread:7 ID:43341256 NUM:7
Thread:8 ID:43341256 NUM:8
Thread:9 ID:43341256 NUM:9
從結果中看到執行的10個線程中所建立的執行個體是同一個,因為執行個體的 id 是相同的,是以執行個體對象所指的記憶體位址是同一個,是同一個執行個體。但是它們的屬性 num 是不同的,這是因為雖然執行個體是同一個,記憶體位址也是一個,但是每個執行個體都初始化了一遍(執行了一遍 __init__ 方法),初始化的時候對執行個體的屬性 num 值進行了覆寫。
它們确确實實是同一個執行個體,但是屬性值卻發生了改變。這就像是“一個人”作為一個執行個體,而這個人改了名字,本來叫“小小”,後來改名叫“大大”,不管叫啥他都是同一個人。
隻初始化一次的單例模式
有些時候我們建立一個單例後并不想讓它修改屬性。我們想第一次建立執行個體完成後,第二次建立的時候直接傳回單例,不要在去執行初始化方法,避免多次初始化帶來的資源和時間開銷。樣例代碼如下所示。
import threading
from concurrent.futures.thread import ThreadPoolExecutor
class Singleton:
_lock_1 = threading.Lock()
_lock_2 = threading.Lock()
_instance = None
__init_flag = False
def __new__(cls, *args, **kwargs):
if cls._instance:
return cls._instance
with cls._lock_1:
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self, num):
if self.__init_flag:
return
with self._lock_2:
if self.__init_flag:
return
self.num = num
self.__init_flag = True
def get_name(self):
return self.num
def run_job(num):
obj = Singleton(num)
print("Thread:%s ID:%s NUM:%s" % (num, id(obj), obj.num))
with ThreadPoolExecutor(max_workers=10) as pool:
for line in range(50):
pool.submit(run_job, line)
# task = []
# for i in range(10):
# t = threading.Thread(target=run_job, args=(i,))
# task.append(t)
#
# for one in task:
# one.start()
#
# for one in task:
# one.join()
執行結果為。
Thread:0 ID:43669064 NUM:0
Thread:1 ID:43669064 NUM:0
Thread:2 ID:43669064 NUM:0
Thread:3 ID:43669064 NUM:0
Thread:4 ID:43669064 NUM:0
Thread:5 ID:43669064 NUM:0
Thread:6 ID:43669064 NUM:0
Thread:7 ID:43669064 NUM:0Thread:8 ID:43669064 NUM:0
Thread:9 ID:43669064 NUM:0