python 单例设计模式

单例模式的应用场景

所有实例对象封装的数据都相同的场景。这样场景下, 应用单例模式可以减少重复创建对象, 从而节约内存。

在数据库的连接过程中, 每次连接过程中所用到的IP, 端口, 用户名 密码等相同。 由于每次单独连接数据库耗时比较长, 通常会先创建一个连接池, 该连接池已经与数据库创建好连接, 其它用户想连接到这个数据库先访问连接池, 由连接池分配连接,从而节约时间。
伪代码

  1 class ConnectionPool:
  2     __instance = None
  3     def __init__(self):
  4         self.addr = '127.0.0.1'
  5         self.port = 8008
  6         self.name = 'sql'
  7         self.conn_list = [1,2,3,4,5,6,7,8,9,10]
  8     def get_connection(self):
  9         r = random.randrange(1, 11)
 10         return r
 11 P =  ConnectionPool()
 12 P.get_connection()

这里写图片描述

每次由客户端连接的时候, 都会创建新的对象。在这种重复创建对象, 且对象中封装的数据相同时候, 即可利用单例模式, 节约内存提升性能

单例模式伪代码:

 class ConnectionPool:
  2     __instance = None
  3     def __init__(self):
  4         self.addr = '127.0.0.1'
  5         self.port = 8008
  6         self.name = 'sql'
  7         self.conn_list = [1,2,3,4,5,6,7,8,9,10]
  8     @staticmethod
  9     def get_instance():
 10         if ConnectionPool.__instance:
 11             return ConnectionPool.__instance
 12         else:
 13             ConnectionPool.__instance == ConnectionPool()
 14             return ConnectionPool.__instance
 15     def get_connection(self):
 16         r = random.randrange(1, 11)
 17         return r

通过一个私有变量,一个静态方法 get_instance()实现了一次创建对象,多次连接。
1. 单例只有一个实例
2. 静态方法、静态字段
3. 所有封装数据都一样时用单例模式

为什么要单例模式

Python 中其他的使用场景

  • python 的logger 就是一个单例模式, 用以日志记录
  • windows 的资源管理是一个单例模式
  • 线程池, 数据库连接池等资源池也是用单例模式
  • 网站数据统计

什么情况下才会使用单例模式:

  • 当每个实例都会占用资源, 而且实例初始化会影响性能, 这个时候可以考虑使用单例模式, 好处就是只实例化一次, 只需要初始化一次
  • 当有同步需要的时候, 可以通过一个实例来进行同步控制, 比如对某个共享文件(如 日志文件)的控制, 对计步器的控制

所用使用单例模式的前提是我们的确用一个实例就可以搞定要解决的问题, 而不需要多个实例, 如果每个实例都要维护自己的实例, 这种情况下单例模式不适合。

最简单的实例代码

class Singleton(object):
    __instance = None

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


s1 = Singleton()
s2 = Singleton()

print(s1)
print(s2)

结果

<__main__.Singleton object at 0x0000027060C48A20>
<__main__.Singleton object at 0x0000027060C48A20>

可以看出返回的结果都是同一个对象, 下面是一个 接近一个真实场景

class Singleton(object):
    __instance = None

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

    def __init__(self, number):
        self.number = number


s1 = Singleton(2)
s2 = Singleton(9)

print(s1)
print(s2)

print(s1.number)
print(s2.number)

打印结果

<__main__.Singleton object at 0x000001FA2CB60668>
<__main__.Singleton object at 0x000001FA2CB60668>
9
9

可以看到是同一个对象实例, 打出的数据都是9 后面修改的也影像前面的值, 共享实例的变量

解决异步 并发问题

有共享数据就要想到并发访问的时候 数据问题。 当有多个线程同时去初始化对象的时候, 有可能就会同时判断 __instance is None, 从而进入初始化instance 的代码中。 为了解决这个问题必须通过同步锁来完成。

import threading
try:
    from synchronize import make_synchronized
except ImportError:
    def make_synchronized(func):
        import threading
        func.__lock__ = threading.Lock()

        def synced_func(*args, **kws):
            with func.__lock__:
                return func(*args, **kws)

        return synced_func


class Singleton(object):
    instance = None

    @make_synchronized
    def __new__(cls, *args, **kwargs):
        if cls.instance is None:
            cls.instance = object.__new__(cls, *args, **kwargs)
        return cls.instance

    def __init__(self):
        self.blog = "xiaorui.cc"

    def go(self):
        pass


def worker():
    e = Singleton()
    print id(e)
    e.go()


def test():
    e1 = Singleton()
    e2 = Singleton()
    e1.blog = 123
    print e1.blog
    print e2.blog
    print id(e1)
    print id(e2)


if __name__ == "__main__":
    test()
    task = []
    for one in range(30):
        t = threading.Thread(target=worker)
        task.append(t)

    for one in task:
        one.start()

    for one in task:
        one.join()

以上伪代码没有进行验证, 但是感觉太复杂啦。

在同一时刻保证只有一个线程访问
保证单例模式的安全

import threading


def synchronized(func):
    func.__lock__ = threading.Lock()

    def lock_func(*args, **kwargs):
        with func.__lock__:
            return func(*args, **kwargs)

    return lock_func


class Singleton(object):
    __instance = None

    @synchronized
    def __new__(cls, *args, **kwargs):
        if cls.__instance is None:
            cls.__instance = super(Singleton, cls).__new__(cls) # 每一次实例化的时候都会返回这同一个instance 对象
        return cls.__instance

    def __init__(self, num):
        self.num = num


a1 = Singleton(2)
a2 = Singleton(9)

print(a1)
print(a2)
print(a1.num)
print(a2.num)

这里使用装饰器的方式

单例模式针对的是同一线程

在tornado 中 只有一个线程,如果是单例模式的话不管你并发多少 单例实例的对象都是同一个, 这个测试过的。

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