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

  • 相关阅读:
    织梦开发——相关阅读likeart应用
    织梦标签教程
    织梦专题调用代码
    HIT 2543 Stone IV
    POJ 3680 Intervals
    HIT 2739 The Chinese Postman Problem
    POJ 1273 Drainage Ditches
    POJ 2455 Secret Milking Machine
    SPOJ 371 Boxes
    HIT 2715 Matrix3
  • 原文地址:https://www.cnblogs.com/c-x-a/p/9483400.html
Copyright © 2011-2022 走看看