zoukankan      html  css  js  c++  java
  • Python设计模式中单例模式的实现及在Tornado中的应用

    单例模式的实现方式
    将类实例绑定到类变量上
    class Singleton(object):
    _instance = None

    def new(cls, *args):
    if not isinstance(cls._instance, cls):
    cls._instance = super(Singleton, cls).new(cls, *args)
    return cls._instance

    但是子类在继承后可以重写__new__以失去单例特性

    class D(Singleton):

    def new(cls, *args):
    return super(D, cls).new(cls, *args)

    使用装饰器实现

    def singleton(_cls):
    inst = {}

    def getinstance(args, **kwargs):
    if _cls not in inst:
    inst[_cls] = _cls(
    args, **kwargs)
    return inst[_cls]
    return getinstance

    @singleton
    class MyClass(object):
    pass

    问题是这样装饰以后返回的不是类而是函数,当然你可以singleton里定义一个类来解决问题,但这样就显得很麻烦了

    使用__metaclass__,这个方式最推荐

    class Singleton(type):
    _inst = {}

    def call(cls, args, **kwargs):
    if cls not in cls._inst:
    cls._inst[cls] = super(Singleton, cls).call(
    args)
    return cls._inst[cls]

    class MyClass(object):
    metaclass = Singleton

    Tornado中的单例模式运用
    来看看tornado.IOLoop中的单例模式:
    class IOLoop(object):

    @staticmethod
    def instance():
    """Returns a global IOLoop instance.

    Most applications have a single, global IOLoop running on the
    main thread. Use this method to get this instance from
    another thread. To get the current thread's IOLoop, use current().
    """
    if not hasattr(IOLoop, "_instance"):
    with IOLoop._instance_lock:
    if not hasattr(IOLoop, "_instance"):
    # New instance after double check
    IOLoop._instance = IOLoop()
    return IOLoop._instance

    为什么这里要double check?来看个这里面简单的单例模式,先来看看代码:

    class Singleton(object):

    @staticmathod
    def instance():
    if not hasattr(Singleton, '_instance'):
    Singleton._instance = Singleton()
    return Singleton._instance

    在 Python 里,可以在真正的构造函数__new__里做文章:

    class Singleton(object):

    def new(cls, *args, **kwargs):
    if not hasattr(cls, '_instance'):
    cls._instance = super(Singleton, cls).new(cls, *args, **kwargs)
    return cls._instance

    这种情况看似还不错,但是不能保证在多线程的环境下仍然好用,看图:
    201632180733229.png (683×463)

    出现了多线程之后,这明显就是行不通的。

    1.上锁使线程同步
    上锁后的代码:

    import threading

    class Singleton(object):

    _instance_lock = threading.Lock()

    @staticmethod
    def instance():
    with Singleton._instance_lock:
    if not hasattr(Singleton, '_instance'):
    Singleton._instance = Singleton()
    return Singleton._instance

    这里确实是解决了多线程的情况,但是我们只有实例化的时候需要上锁,其它时候Singleton._instance已经存在了,不需要锁了,但是这时候其它要获得Singleton实例的线程还是必须等待,锁的存在明显降低了效率,有性能损耗。

    2.全局变量
    在 Java/C++ 这些语言里还可以利用全局变量的方式解决上面那种加锁(同步)带来的问题:

    class Singleton {

    private static Singleton instance = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {
    return instance;
    }

    }

    在 Python 里就是这样了:

    class Singleton(object):

    @staticmethod
    def instance():
    return _g_singleton

    _g_singleton = Singleton()

    def get_instance():

    return _g_singleton

    但是如果这个类所占的资源较多的话,还没有用这个实例就已经存在了,是非常不划算的,Python 代码也略显丑陋……

    所以出现了像tornado.IOLoop.instance()那样的double check的单例模式了。在多线程的情况下,既没有同步(加锁)带来的性能下降,也没有全局变量直接实例化带来的资源浪费。

    3.装饰器

    如果使用装饰器,那么将会是这样:
    import functools

    def singleton(cls):
    ''' Use class as singleton. '''

    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

    Sample use:

    @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

    def singleton(cls):
    instance = cls()
    instance.call = lambda: instance
    return instance

    Sample use

    @singleton
    class Highlander:
    x = 100

    Of course you can have any attributes or methods you like.

    Highlander() is Highlander() is Highlander #=> True
    id(Highlander()) == id(Highlander) #=> True
    Highlander().x == Highlander.x == 100 #=> True
    Highlander.x = 50
    Highlander().x == Highlander.x == 50 #=> True

  • 相关阅读:
    ecmascript6入门
    优化代码,引发了早期缺陷导致新bug
    realm
    Hypervisor scheduler
    WEEX学习网站
    JavaScript while 循环
    JavaScript for 循环
    JavaScript switch 语句
    JavaScript If…Else 语句
    JavaScript 比较和逻辑运算符
  • 原文地址:https://www.cnblogs.com/c-x-a/p/9483400.html
Copyright © 2011-2022 走看看