Python单例设计模式

2019-12-18  本文已影响0人  还是那个没头脑

单例模式的使用场景

从这些使用场景我们可以总结下什么情况下需要单例模式:

  1. 当每个实例都会占用资源,而且实例初始化会影响性能,这个时候就可以考虑使用单例模式,它给我们带来的好处是只有一个实例占用资源,并且只需初始化一次;
  2. 当有同步需要的时候,可以通过一个实例来进行同步控制,比如对某个共享文件(如日志文件)的控制,对计数器的同步控制等,这种情况下由于只有一个实例,所以不用担心同步问题。

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

Python来实现一个单例模式

class Singleton(object):
    __instance = None
    def __new__(cls, *args, **kwargs):  # 这里不能使用__init__,因为__init__是在instance已经生成以后才去调用的
        if cls.__instance is None:
            cls.__instance = super(Singleton, cls).__new__(cls)
        return cls.__instance

a = Singleton()
b = Singleton()

print(a)
print(b)
print(id(a))
print(id(b))

打印结果如下:

<__main__.singleton object at 0x017EEFF0>
<__main__.singleton object at 0x017EEFF0>
25096176
25096176

可以看出两次创建对象,结果返回的是同一个对象实例,我们再让我们的例子更接近真实的使用场景来看看

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, status_number):
        self.status_number = status_number


s1 = Singleton(2)
s2 = Singleton(5)
print s1
print s2

print s1.status_number
print s2.status_number

这里我们使用了init方法,下面是打印结果,可以看出确实是只有一个实例,共享了实例的变量

<__main__.Singleton object at 0x7f5116865490>
<__main__.Singleton object at 0x7f5116865490>
5
5

这个例子中有一个问题我们没有解决,那就是多线程的问题,当有多个线程同时去初始化对象时,就很可能同时判断__instance is None,从而进入初始化instance的代码中。所以为了解决这个问题,我们必须通过同步锁来解决这个问题。以下例子来自xiaorui

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()

更简单地使用单例模式

在Python的官方网站给了两个例子是用装饰符来修饰类,从而使得类变成了单例模式,使得我们可以通过更加简单的方式去实现单例模式
官网Singleton

第一种装饰器写法(简单)
def singleton (cls, *args, **kwargs):

    instances = {}

    def get_instance (*args, **kwargs):

        if cls not in instances:

            instances[cls] = cls(*args, **kwargs)

        return instances[cls]

    return get_instance

代码分析:
第1行,创建外层函数singleton,可以传入类

第3行,创建一个instances字典用来保存单例

第5行,创建一个内层函数来获得单例

第7,9,11行, 判断instances字典中是否含有单例,如果没有就创建单例并保存到instances字典中,然后返回该单例

第13行, 返回内层函数get_instance

@singleton
class Student(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age
 
    def run(self):
        print(self.name)
 
if __name__ == '__main__':
    student = Student('wang', 18)
    student.run()
    hshs = Student('kai', 21)
    hshs.run()
 
 
执行结果:
wang
wang

student = Student('wang', 18) 即相当于直接把 Student('wang', 18) 作用 singleton的第一个参数传入方法中,此时instance 为空,当这个类第一次实例化之后 instalce = {<class 'main.Student'>:<main.Student object at 0x0000026CFBA5CF60>}

第二种装饰器写法(复杂)
import functools

def singleton(cls):
    """
    将一个类作为单例
    """

    cls.__new_original__ = cls.__new__

    @functools.wraps(cls.__new__)
    def singleton_new(cls, *args, **kw):
        it = cls.__dict__.get('__it__')
        if it is not None:
            return it

        cls.__it__ = it = cls.__new_original__(cls, *args, **kw)
        it.__init_original__(*args, **kw)
        return it

    cls.__new__ = singleton_new
    cls.__init_original__ = cls.__init__
    cls.__init__ = object.__init__

    return cls

@ singleton
class Foo:
    def __new__(cls):
        cls.x = 10
        return object.__new__(cls)

    def __init__(self):
        assert self.x == 10
        self.x = 15

assert Foo().x == 15

Foo().x = 20

assert Foo().x == 20
上一篇下一篇

猜你喜欢

热点阅读