Python实现设计模式--01.单例模式(Singleton Pattern)

1、基于模块引用(饱汉模式)

建立文件Emperor_Singleton.py,内容如下:

# 皇帝类
class Emperor(object):
    def say(self):
        print('我是唯一的皇帝!')


emperor = Emperor()

测试内容如下:

from singleton.Emperor_Singleton import emperor

emperor.say()

运行测试类,打印结果:

我是唯一的皇帝!

2、基于__new__方法,继承父单例类(饿汉模式,  __new__方法类似其他语言的构造函数)

建立文件Singleton.py,内容如下(父单例类):

class Singleton:
    __instance = None

    def __new__(cls, *args, **kwargs):
        if Singleton.__instance is None:
            Singleton.__instance = object.__new__(cls, *args)
        return Singleton.__instance

测试内容如下:

import random

from singleton.Singleton import Singleton


class Emperor(Singleton):
    def __init__(self):
        if not hasattr(self, 'name'):
            self.name = random.choice(['秦始皇', '汉武帝', '康熙', '汉献帝'])

    def say(self):
        print('我是皇帝:', self.name, ',我的id:', id(self))


for i in range(10):
    Emperor().say()
运行测试类,打印结果:
我是皇帝: 汉献帝 , 我的id: 4330069576
我是皇帝: 汉献帝 , 我的id: 4330069576
我是皇帝: 汉献帝 , 我的id: 4330069576
我是皇帝: 汉献帝 , 我的id: 4330069576
我是皇帝: 汉献帝 , 我的id: 4330069576
我是皇帝: 汉献帝 , 我的id: 4330069576
我是皇帝: 汉献帝 , 我的id: 4330069576
我是皇帝: 汉献帝 , 我的id: 4330069576
我是皇帝: 汉献帝 , 我的id: 4330069576
我是皇帝: 汉献帝 , 我的id: 4330069576

3、基于__new__方法的线程安全性

上面第2点基于__new__方法,存在线程安全问题。我们实验下,将Singleton类创建对象的时间延长,如下:

class Singleton:
    __instance = None

    def __new__(cls, *args, **kwargs):
        if Singleton.__instance is None:
            for i in range(5000000):
                continue
            Singleton.__instance = object.__new__(cls, *args)
        return Singleton.__instance

测试文件创建10个线程,如下:

import random
import threading

from singleton.Singleton import Singleton


class Emperor(Singleton):
    def __init__(self):
        if not hasattr(self, 'name'):
            self.name = random.choice(['秦始皇', '汉武帝', '康熙', '汉献帝'])

    def say(self):
        print('我是皇帝:', self.name, ', 我的id:', id(self))


class MyThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        Emperor().say()


if __name__ == '__main__':
    threads = []
    for i in range(10):
        threads.append(MyThread())

    for t in threads:
        t.start()

    for t in threads:
        t.join()
运行测试类,打印结果:
我是皇帝: 汉武帝 , 我的id: 4330917960
我是皇帝: 康熙 , 我的id: 4330918016
我是皇帝: 秦始皇 , 我的id: 4330917960
我是皇帝: 秦始皇 , 我的id: 4330918016
我是皇帝: 秦始皇 , 我的id: 4330917960
我是皇帝: 汉献帝 , 我的id: 4330918016
我是皇帝: 秦始皇 , 我的id: 4330917960
我是皇帝: 秦始皇 , 我的id: 4330918016
我是皇帝: 秦始皇 , 我的id: 4330917960
我是皇帝: 秦始皇 , 我的id: 4330918016

可以看出,这里显然是线程不安全的……

4、编写线程安全的饿汉单例,基于__new__方法

修改Singleton类,加入双重锁机制:

import threading


class Singleton:
    __instance = None
    __lock = threading.Lock()

    def __new__(cls, *args, **kwargs):
        if Singleton.__instance is None:
            Singleton.__lock.acquire()
            if Singleton.__instance is None:
                for i in range(5000000):
                    continue
                Singleton.__instance = object.__new__(cls, *args)
            Singleton.__lock.release()
        return Singleton.__instance

还是用上面的测试文件,结果如下:

我是皇帝: 康熙 , 我的id: 4330938552
我是皇帝: 康熙 , 我的id: 4330938552
我是皇帝: 康熙 , 我的id: 4330938552
我是皇帝: 康熙 , 我的id: 4330938552
我是皇帝: 康熙 , 我的id: 4330938552
我是皇帝: 康熙 , 我的id: 4330938552
我是皇帝: 康熙 , 我的id: 4330938552
我是皇帝: 康熙 , 我的id: 4330938552
我是皇帝: 康熙 , 我的id: 4330938552
我是皇帝: 康熙 , 我的id: 4330938552

至此,一个标准的基于__new__方法的单例模式就完成啦!

你可能感兴趣的:(设计模式-python)