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