本系列文章是希望将软件项目中最常见的设计模式用通俗易懂的语言来讲解清楚,并通过Python来实现,每个设计模式都是围绕如下三个问题:

  1. 为什么?即为什么要使用这个设计模式,在使用这个模式之前存在什么样的问题?
  2. 是什么?通过Python语言来去实现这个设计模式,用于解决为什么中提到的问题。
  3. 怎么用?理解了为什么我们也就基本了解了什么情况下使用这个模式,不过在这里还是会细化使用场景,阐述模式的局限和优缺点。

这一篇我们先来看看单例模式。单例模式是设计模式中逻辑最简单,最容易理解的一个模式,简单到只需要一句话就可以理解,即“保证只有一个对象实例的模式”。问题的关键在于实现起来并没有想象的那么简单。不过我们还是先来讨论下为什么需要这个模式吧。

为什么

我们首先来看看单例模式的使用场景,然后再来分析为什么需要单例模式。

  • Python的logger就是一个单例模式,用以日志记录
  • Windows的资源管理器是一个单例模式
  • 线程池,数据库连接池等资源池一般也用单例模式
  • 网站计数器

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

  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, *args, **kwargs)
return cls.__instance s1 = Singleton()
s2 = Singleton()
print s1
print s2

打印结果如下:

<__main__.Singleton object at 0x7f3580dbe110>
<__main__.Singleton object at 0x7f3580dbe110>

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

class Singleton(object):
__instance = None def __new__(cls, *args, **kwargs):
if cls.__instance is None:
cls.__instance = super(
Singleton, cls).__new__(cls, *args, **kwargs)
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):
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

这里简单解释下:

  1. 在定义class Highlander的时候已经执行完所有singleton装饰器中的代码,得到了一个instance,所以这之后所有对Highlander的调用实际上是在调用instance的_call_ 方法。
  2. 我们通过lambda函数定义了_call_方法让它始终返回instance,因此Highlander()和Highlander都返回instance
  3. 同时由于在类定义代码执行时就已经创建了instance,所以后续不论是多线程还是单线程,在调用Highlander时都是在调用instance的_call_方法,也就无需同步了。
    最后我想说的是这种方法简直碉堡了~~~
    附上我用于多线程的测试代码
import threading

def singleton(cls):
instance = cls()
instance.__call__ = lambda: instance
return instance @singleton
class Highlander:
x = 100
# Of course you can have any attributes or methods you like. def worker():
hl = Highlander()
hl.x += 1
print hl
print hl.x def main():
threads = []
for _ in xrange(50):
t = threading.Thread(target=worker)
threads.append(t) for t in threads:
t.start() for t in threads:
t.join() if __name__ == '__main__':
main()

这里的代码有一点小问题,就是在打印的时候有可能x属性已经被别的线程+1了,所以有可能导致同一个数打印多次,而有的数没有打印,但是不影响最终x属性的结果,所以当所有线程结束之后,属性x最终的值是可以保证正确的。

Reference:

 

作者:geekpy
链接:https://www.jianshu.com/p/ec6589e02e2f
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

设计模式(Python)-单例模式的更多相关文章

  1. python设计模式之单例模式(一)

    前言 单例模式是创建模式中比较常见和常用的模式,在程序执行的整个生命周期只存在一个实例对象. 系列文章 python设计模式之单例模式(一) python设计模式之常用创建模式总结(二) python ...

  2. python设计模式之单例模式(二)

    上次我们简单了解了一下什么是单例模式,今天我们继续探究.上次的内容点这 python设计模式之单例模式(一) 上次们讨论的是GoF的单例设计模式,该模式是指:一个类有且只有一个对象.通常我们需要的是让 ...

  3. python设计模式之单例模式(转)

    设计模式之单例模式 单例设计模式是怎么来的?在面向对象的程序设计中,当业务并发量非常大时,那么就会出现重复创建相同的对象,每创建一个对象就会开辟一块内存空间,而这些对象其实是一模一样的,那么有没有办法 ...

  4. python_way,day8 面向对象【多态、成员--字段 方法 属性、成员修饰符、特殊成员、异常处理、设计模式之单例模式、模块:isinstance、issubclass】

    python_way day8 一.面向对象三大特性: 多态 二.面向对象中的成员 字段.方法属性 三.成员修饰符 四.特殊成员 __init__.__doc__.__call__.__setitem ...

  5. 文成小盆友python-num8 面向对象中的成员,成员修饰符,特殊成员,异常处理,设计模式之单例模式

    本节主要内容: 1.面向对象中的成员 2.成员修饰符 3.特殊成员 4.异常处理 5.设计模式之单例模式 一.面向对象中的成员(类的成员) 类的成员总共可以分为3大类,每类中有不同的分支. 1.总述, ...

  6. 大话设计模式--Python

    作者:五岳 出处:http://www.cnblogs.com/wuyuegb2312 上一周把<大话设计模式>看完了,对面向对象技术有了新的理解,对于一个在C下写代码比较多.偶尔会用到一 ...

  7. python单例模式的实现与优化

    python单例模式的实现与优化 阅读目录(Content) 单例模式 实现单例模式的几种方式 1.使用模块 2.使用装饰器 3.使用类 4.基于__new__方法实现(推荐使用,方便) 5.基于me ...

  8. 设计模式之单例模式(Singleton)

    设计模式之单例模式(Singleton) 设计模式是前辈的一些经验总结之后的精髓,学习设计模式可以针对不同的问题给出更加优雅的解答 单例模式可分为俩种:懒汉模式和饿汉模式.俩种模式分别有不同的优势和缺 ...

  9. GJM : C#设计模式(1)——单例模式

    感谢您的阅读.喜欢的.有用的就请大哥大嫂们高抬贵手"推荐一下"吧!你的精神支持是博主强大的写作动力以及转载收藏动力.欢迎转载! 版权声明:本文原创发表于 [请点击连接前往] ,未经 ...

随机推荐

  1. spring AOP的注解实例

    上一篇写了spring AOP 的两种代理,这里开始AOP的实现了,个人喜欢用注解方式,原因是相对于XML方式注解方式更灵活,更强大,更可扩展.所以XML方式的AOP实现就被我抛弃了. 实现Sprin ...

  2. 使用U盘安装linux系统

    1. 下载并安装:UltraISO.流氓软件太多,最后是在百度软件中心下的,推荐. 2. 插入U盘.打开UltraISO,然后:文件 -- 打开 -- 选择你的ISO文件,打开. 3. UltraIS ...

  3. 【转载】IEEE754 学习总结

    以下是参考<IEEE754 学习总结>并结合自己学习总结 一:前言二:预备知识 三:浮点数的表示范围四:将浮点格式转换成十进制数 一:前言 前不久在分析一个程序的过程中遇到了浮点运算,也就 ...

  4. POJ 3268 Silver Cow Party(最短路&Dijkstra)题解

    题意:有n个地点,有m条路,问从所有点走到指定点x再走回去的最短路中的最长路径 思路:用Floyd超时的,这里用的Dijkstra. Dijkstra感觉和Prim和Kruskal的思路很像啊.我们把 ...

  5. python判断key是否存在

    d = {: , : , : , : , : , : } def is_key_present(x): if x in d: print('Key is present in the dictiona ...

  6. js中对象的一些特性,JSON,scroll家族

    一.js中对象的一些特性 对象的动态特性 1.当对象有这个属性时,会对属性的值重写 2.当对象没有这个属性时,会为对象创建一个新属性,并赋值 获得对象的属性的方式 为元素设置DOM0级事件 二.JSO ...

  7. Learn Rails5.2- ActiveRecord: Migration , spring的使用(不兼容的解决办法)

    偶然一次: 运行rails generate停止不动,网上查找答案,可能是bundle update 之后 spring 版本变化了,和正在运行的 spring 实例不兼容. Spring导致的同样的 ...

  8. UVA-10305 Ordering Tasks (拓扑排序)

    题目大意:给出n个点,m条关系,按关系的从小到大排序. 题目分析:拓扑排序的模板题,套模板. kahn算法: 伪代码: Kahn算法: 摘一段维基百科上关于Kahn算法的伪码描述: L← Empty ...

  9. web 移动端事件总结

    1.https://www.jianshu.com/p/6f85e957a725 (web 移动端事件总结)

  10. Android 遍历全国的地区二(获取天气)

    根据上次的内容 1. 界面布局 weather_layout.xml <LinearLayout xmlns:android="http://schemas.android.com/a ...