脚本之家,脚本语言编程技术及教程分享平台!
分类导航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服务器之家 - 脚本之家 - Python - Python设计模式中单例模式的实现及在Tornado中的应用

Python设计模式中单例模式的实现及在Tornado中的应用

2020-08-15 12:00Damnever Python

这篇文章主要介绍了Python设计模式中单例模式的实现及在Tornado中的应用,讲解了单例模式用于设计Tornado框架中的线程控制方面的相关问题,需要的朋友可以参考下

单例模式的实现方式
将类实例绑定到类变量上

?
1
2
3
4
5
6
7
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__以失去单例特性

?
1
2
3
4
class D(Singleton):
 
  def __new__(cls, *args):
    return super(D, cls).__new__(cls, *args)

使用装饰器实现

?
1
2
3
4
5
6
7
8
9
10
11
12
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__,这个方式最推荐

?
1
2
3
4
5
6
7
8
9
10
11
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中的单例模式:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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?来看个这里面简单的单例模式,先来看看代码:

?
1
2
3
4
5
6
7
class Singleton(object):
 
  @staticmathod
  def instance():
    if not hasattr(Singleton, '_instance'):
      Singleton._instance = Singleton()
    return Singleton._instance

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

?
1
2
3
4
5
6
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

这种情况看似还不错,但是不能保证在多线程的环境下仍然好用,看图:

Python设计模式中单例模式的实现及在Tornado中的应用

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
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++ 这些语言里还可以利用全局变量的方式解决上面那种加锁(同步)带来的问题:

?
1
2
3
4
5
6
7
8
9
10
11
class Singleton {
 
  private static Singleton instance = new Singleton();
  
  private Singleton() {}
  
  public static Singleton getInstance() {
    return instance;
  }
  
}

在 Python 里就是这样了:

?
1
2
3
4
5
6
7
8
9
10
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.装饰器

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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

 

延伸 · 阅读

精彩推荐