目录
单例模式定义:
维基百科对单例模式定义如下:
单例模式,也叫单子模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。
import 模块:
其实python的import module就是一种典型的单例模式。请看下面这个例子:
# global_var.py
COUNT = 0
我们写一个多线程的测试程序,每个线程都把COUNT加一,并且打印出来
In [1]: from multiprocessing.dummy import Pool
In [2]: p = Pool(10)
In [3]: def count(x):
...: import global_var
...: myglobal.COUNT += 1
...: print global_var.COUNT
In [4]: p.map(count, range(10))
1
3
5
2
6
7
9
8
10
4
Out[4]: [None, None, None, None, None, None, None, None, None, None]
In [5]: import global_var
In [6]: global_var.COUNT
Out[6]: 10
可以看到,在每个线程中都把COUNT加了一,得到不同的值。在最后也按照预期得到了COUNT值为10。
我们再看一下每个线程里面global_var的内存地址,就明白为什么说它是单例模式了。
In [1]: from multiprocessing.dummy import Pool
In [2]: p = Pool(10)
In [3]: def imp(x):
import global_var
print id(global_var)
...:
In [4]: p.map(imp, range(10))
140519408948848
140519408948848
140519408948848
140519408948848
140519408948848
140519408948848
140519408948848
140519408948848
140519408948848
140519408948848
Out[4]: [None, None, None, None, None, None, None, None, None, None]
可以看到,print出来地址值是相同的。
现在让我们想想import的用法,就可以明白为什么它要设计成这样了。
我们都知道,import一个模块之后,会在程序的不同地方调用这个模块,可能在不同的函数,也可能在不同的线程中。我们希望无论在什么地方,这个模块的行为是一致的,也就是说,我们希望它是唯一的,而不是一个独立的个体。
logging模块
logging
是单例模式的一个非常典型的应用,因为我们的应用内部一般不去关心logging的细节,比如写到数据库还是文件,时间戳的格式等等。在应用内部,我们只关心logging的分类,内容和级别,是app的还是db的log,是warning,error还是debug。
而logging的配置,则是全局的,一个进程中任何地方对logging配置的修改(如增加输出到文件),都会影响到所有使用logging的地方。那么它是怎么做到的呢?
例子:
import logging
# 全局的配置
logging.basicConfig(format="%(asctime)s %(levelname)s %(name)s %(message)s", level='DEBUG')
# 在函数中如下使用
logging.debug('hello world')
# 在其他module中,sub.py
import logging
logging.info('this is the test in module!')
打印结果为:
2018-10-17 10:26:33,100 DEBUG root hello world
2018-10-17 10:26:34,274 INFO root this is test in module!
上面两次log的输出格式一致, 让我们来看看logging的源码
root = RootLogger(WARNING)
def info(msg, *args, **kwargs):
"""
Log a message with severity 'INFO' on the root logger.
"""
if len(root.handlers) == 0:
basicConfig()
root.info(msg, *args, **kwargs)
可以看到具体的执行都是由root这个实例来进行的,由于root是一个module层面的实例,并不在info函数内部实例化,所以所有的logging.info都是同一个实例来进行的。
看到单例模式的使用场景, 那么我们如何自己实现单例模式呢?
实现单例模式的几种方式:
1.使用模块方法
既然Python 的模块就是天然的单例模式, 那我们可以利用模块这一特性轻松实现单例模式, 如下:
mysingleton.py
class Singleton(object):
def foo(self):
pass
singleton = Singleton()
from mysingleton import singleton
2.使用装饰器
def Singleton(cls):
_instance = {}
def _singleton(*args, **kargs):
if cls not in _instance:
_instance[cls] = cls(*args, **kargs)
return _instance[cls]
return _singleton
@Singleton
class A(object):
a = 1
def __init__(self, x=0):
self.x = x
a1 = A(2)
a2 = A(3)
3.使用类
class Singleton(object):
def __init__(self):
pass
@classmethod
def instance(cls, *args, **kwargs):
if not hasattr(Singleton, "_instance"):
Singleton._instance = Singleton(*args, **kwargs)
return Singleton._instance
4.基于__new__方法实现(推荐使用,方便)
我们知道,当我们实例化一个对象时,是先执行了类的__new__方法(我们没写时,默认调用object.__new__),实例化对象;然后再执行类的__init__方法,对这个对象进行初始化,所有我们可以基于这个,实现单例模式
class Singleton(object):
def __init__(self):
pass
def __new__(cls, *args, **kwargs):
if not hasattr(Singleton, "_instance"):
Singleton._instance = object.__new__(cls)
return Singleton._instance
obj1 = Singleton()
obj2 = Singleton()
print(obj1,obj2)
采用这种方式的单例模式,以后实例化对象时,和平时实例化对象的方法一样 obj = Singleton()
5.基于metaclass方式实现
class SingletonType(type):
def __call__(cls, *args, **kwargs):
if not hasattr(cls, "_instance"):
cls._instance = super(SingletonType,cls).__call__(*args, **kwargs)
return cls._instance
class Foo(metaclass=SingletonType):
def __init__(self,name):
self.name = name
obj1 = Foo('name')
obj2 = Foo('name')
print(obj1, obj2)