要点概论:

1. 理解单例设计模式

2. 单例模式中的懒汉式实例化

3. 模块级别的单例模式

4. Monostate单例模式

5. 单例和元类

6.单例模式Ⅰ

7. 单例模式Ⅱ

8. 单例模式的缺点

9. 总结

引言:

  单例设计模式是应用开发过程中最简单和最著名的一种创建型设计模式。除此之外还会介绍 Monostate(或者Borg)设计模式,它是单例设计模式的一个变种。

1. 理解单例设计模式

  在单例模式中,类有且只有一个特定类型的对象,并提供全局访问点。

  通常用于日志记录或数据库操作,打印机后台处理程序等。

  在程序运行过程中只能生成一个实例,以避免对同一资源产生相互冲突的请求。

  例如:使用一个数据库对象对数据库进行操作,以维护数据的一致性;或者希望使用一个日志类的对象,将多项服务的日志信息按照顺序转储到一个特定的日志文件中。

  单例设计模式的意图如下

  1)确保类有且只有一个对象被创建

  2)为对象提供一个访问点,以使程序可以全局访问该对象

  3)控制共享资源的并行访问

  (单例设计模式的 UML 类图)(百度百科链接)(UML 的 9 种图例解析)

  实现单例模式的一个简单方法是,使构造函数私有化,并创建一个静态方法来完成对象的初始化。

  这样,对象在第一次调用时创建,此后,这个类将返回一个对象。

  PS:在 Python 中,因为它无法创建私有的构造函数,所以接下来,我们来看看 Python 语言如何实现单例模式。

单例模式

  上例中,通过覆盖 __new__ 方法(python 用于实例化对象的特殊方法)来控制对象的创建。并在创建之前,先检查对象是否已存在。

  方法 hasattr 用于查看对象 cls 是否具有属性 instance ,该属性的作用是检查该类是否已经生成了一个对象。

  可以看到,当 s1 被请求的时候,hasattr() 发现对象已经存在,所以,对象s1将被分配已有的对象实例(即原来的内存地址)。

2. 单例模式中的懒汉式实例化

  懒汉化实例能确保在时机需要时才创建对象,所以,懒汉化实例化时一种节约资源并仅在需要时才创建它们的方式。

懒汉式实例化

  在上例中,执行 s = singleton() 的时候,它会调用 __init__ 方法,但没有新的对象被创建。实际的对象创建发生在调用 Singleton.getInstance() 的时候。

  【在pycharm里用dubug跑一遍试试】

3. 模块级别的单例模式

  默认情况下,所有的模块都是单例,这是由 python 的导入行为所决定的。

  

  步骤如下:

  1)检查一个 python 模块是否被导入

  2)如果已经导入,则返回该模块的对象。如果还没有导入,则导入该模块,并实例化。

  3)因此,当模块被导入的时候,它就会被初始化。接着当同一个模块被再次导入的时候,它会返回同一个对象。

 

4. Monostate 单例模式

  不同实例占用不同的内存地址,但是共享相同的状态,称为 Monostate (单态)模式。

Monostate模式

  在上例中,将类变量 __shared_state 赋值给了变量 __dict__ (它时 python 的一个特殊变量,用于存储一个类所有对象的状态)。

  因此,对象 b 的变量 x 发生了变化,这个变化也会复制到被所有对象共享的 __dict__ 变量,即 b1 的变量 x 也会变为 4。

除此之外,我们还可以通过修改 __new__ 方法本身来实现 Borg 模式:

通过__new__实现

5. 单例和元类

  元类时一个类的类,这意味着该类时它的原来的实例。

  在 python 中,一切皆对象。如果 a = 5 ,则 type(a) 返回 <type'int'>,这意味着 a 是 int 类型。但是,type(int) 返回<type'type'>,这表明存在一个元类,因为 int 是 type 类型的类。

  

  类的定义由它的元类决定,所以当我们用类 A 创建一个类时, python 通过 A = type(name,bases,dict) 创建它。

    ● name:这是类的名称

    ● basa:这是基类

    ● dict:这是属性变量

  下面是python 3.5 中的一个示例元类的实现:

 

示例元类

  在上例中,当我们使用 int(4,5) 实例化 int 类时, MyInt 元类的 __call__ 方法将被调用,这意味着现在元类控制着对象的实例化。

  由于元类对类创建和对象实例化有更多的控制权,所以它同样可以用于创建单例(注意:为了控制类的创建和初始化,元类将覆盖 __new__ 和 __init__ 方法):

基于元类的单例实现

6.单例模式Ⅰ

  下面通过一个数据库应用程序来展示单例的应用。

  不妨以需要对数据库进行多种读取和写入操作的云服务为例进行讲解。

  完整的云服务被分为多个服务,每个服务执行不同的数据库操作。针对 UI (Web应用程序)上的操作将导致调用 API ,最终产生相应的 DB 操作。

  要注意的是,跨不同服务的共享资源是数据库本身。因此需要注意以下几点:

     ● 数据库操作的一致性,即一个操作不应与其他操作发生冲突。

       ● 优化数据库的各种操作,以提高内存和 CPU 的利用率。

  这里提供一个python的实现:

import sqlite3
class MetaSingleton(type):__instances = {}def __call__(cls, *args, **kwargs):if cls not in cls.__instances:cls.__instances[cls] = super(MetaSingleton,cls).__call__(*args,**kwargs)return cls.__instances[cls]class Database(metaclass=MetaSingleton):connection = Nonedef connect(self):if self.connection is None:self.connection = sqlite3.connect('db.sqlite3')self.cursorobj = self.connection.cursor()return self.cursorobjdb1 = Database().connect()
db2 = Database().connect()print('Database object db1',db1)
print('Database object db2',db2)# 程序运行结果如下
# Database object db1 <sqlite3.Cursor object at 0x0000019321AA3180>
# Database object db2 <sqlite3.Cursor object at 0x0000019321AA3180>

View Code

  上例需要明白以下几点:

  1)首先以 MetaSingleton 为名创建了一个远了【python 的特殊方法 __call__ 可以通过元类创建单例】。

  2)数据库类由 MetaSingleton 类装饰后,其行为就会表现为单例。因此,当数据库类被实例化时,它只创建一个对象。

  3)当 Web 应用程序对数据库执行某些操作时,它会多次实例化数据库类,但只创建一个对象。因为只有一个对象,所以对数据库的调用是同步的。

    此外,这样还能够节约系统资源,并且可以避免消耗过多的内存或 CPU 资源。

  PS:假如我们要开发的不是单个 Web 应用程序,而是集群化的情形,即多各 Web 应用共享单个数据库。

    在这种情况下,每增加一个 Web 应用程序,就要新建一个单例,添加一个新的对象来查询数据库,这导致数据库操作无法同步,并且要耗费大量的资源,这时候,数据库连接池比实现单例要好得多。

7. 单例模式Ⅱ

   接下来是一个为基础设施提供运行状况监控服务的例子。

  在下面的代码中,hc1 和 hc2 对象与单例中的类相同。

class HealthCheck:__instance = Nonedef __new__(cls, *args, **kwargs):if not HealthCheck.__instance:HealthCheck.__instance = super(HealthCheck,cls).__new__(cls,*args,**kwargs)return HealthCheck.__instancedef __init__(self):self._servers = []def addServer(self):self._servers.append('Server 1')self._servers.append('Server 2')self._servers.append('Server 3')self._servers.append('Server 4')def changeServer(self):self._servers.pop()self._servers.append('Server 5')hc1 = HealthCheck()
hc2 = HealthCheck()hc1.addServer()
print('第一次检查...')
for i in range(4):print('Checking',hc1._servers[i])hc2.changeServer()
print('第二次检查')
for j in range(4):print('Checking',hc2._servers[j])# 程序运行结果如下:
# 第一次检查...
# Checking Server 1
# Checking Server 2
# Checking Server 3
# Checking Server 4
# 第二次检查
# Checking Server 1
# Checking Server 2
# Checking Server 3
# Checking Server 5

View Code

  在上例中,使用 addServer()  方法将服务器添加到基础设施中,以进行运行状况检查。

  首先,通过迭代对这些服务器的运行状况进行检查。之后,changeServer() 方法会删除最后一个服务器【模拟排除故障服务器】,并向计划进行运行状况检查的基础设施中添加一各新服务器。

  因此,当运行状况检查进行第二次迭代时,它会使用修改后的服务器列表。

8. 单例模式的缺点

  由于单例模式具有全局访问权限,因此可能会出现以下问题:

    1)全局变量可能在某处已经被误改,但是开发人员仍然认为它们没有发生变化,而该变量还在应用程序的其他位置将被使用。

    2)可能会对同一个对象创建多个引用。由于单例只创建一个对象,因此这种情况下会对同一个对象创建多个引用。

    3)所有依赖于全局变量的类都会由于一个类的改变而紧密耦合为全局数据,从而可能在无意中影响另一个类。

  

 

9. 总结

  1)在许多实际应用程序中,我们只需要创建一个对象,如线程池,缓存,对话框,注册表设置等。否则会导致资源的过度使用。

  2)单例模式能够在不带来太多缺陷的情况下提供全局访问点。

  3)但是,当使用全局变量或类的实例化非常耗费资源并且最终没有用到它们的情况下,单例的影响可以忽略不计。

  

转载于:https://www.cnblogs.com/HZY258/p/8462554.html

python单例设计模式(待补充)相关推荐

  1. Python单例设计模式

    (1)为什么使用设计模式? 使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. (2)单例设计模式概念 让类创建的对象只有唯一的一个实例,即每次执行  类名() 返回的对象,内存地 ...

  2. python:单例设计模式

    学习目标: python单例设计模式 学习内容: 1.设计模式:前人工作的总结和提炼,针对某一特定问题的成熟的解决方案 使用设计模式:可重用代码.让代码更容易被他人理解.保证代码可靠性 2.单例设计模 ...

  3. python基础知识整理 第七节:单例设计模式、异常、模块、包、制作模块、文件

    1.单例设计模式 单例设计模式就是为对象在内存中分配空间的时候,永远只会返回一个唯一的固定的内存空间.这样就能保证在内存中这个类的对象只有唯一的一份,这个就叫做单例.(为对象分配空间使用的是内置方法_ ...

  4. Python(25)-单例设计模式

    单例设计模式 1.单例设计模式 2.__new__方法 3.初始动作只执行一次 本系列博文来自学习<Python基础视频教程>笔记整理,视屏教程连接地址:http://yun.itheim ...

  5. Python 学习笔记 类的封装 类的继承 多态继承 类方法和静态方法 单例设计模式

    一.类的封装: 1.概念: 广义的封装:函数和类的定义本身,就是封装的体现 狭义的封装:一个类的某些属性,在使用的过程 中,不希望被外界直接访问,而是把这个属性给作为私有的[只有当前类持有],然后暴露 ...

  6. 单例设计模式详解。。。。。。。。。。。

    public class Demo01 { public static void main(String[] args) { // TODO Auto-generated method stub /* ...

  7. 对于java程序语言的单例设计模式讲解

    1.设计模式:解决某一类问题最行之有效的方法.(java中有23种通用设计模式) 单例设计模式:解决一个类在内存中只存在一个对象. 2.单例设计模式有两种方式: 1)饿汉式 先初始化对象.当类一进内存 ...

  8. java软件设计模式只单例设计模式

    概述 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. 毫无疑问,设计 ...

  9. AJPFX关于单例设计模式

    单例设计模式优势:保证一个类在内存中的对象唯一性. 比如:多程序读取一个配置文件时,建议配置文件封装成对象.会方便操作其中数据,又要保证多个程序读到的是同一个配置文件对象,就需要该配置文件对象在内存中 ...

最新文章

  1. django_rest_framework之GenericAPIView(一)源码讲解
  2. 数据库设计与开发概述
  3. C# this关键字的3种用法
  4. 201521123058 软工阅读第二次作业
  5. re.findall的正则参数为什么不能包含元组表达式()或竖线符号,先挖个坑
  6. ora-03297(表空间无法回收)问题的解决方法
  7. 关于校验与确认两步操作时,新增,修改需要考虑的问题
  8. Oracle查询结果随机排序(去重,抽检)
  9. 虚拟仿真实训教学管理及资源共享平台虚拟实训开发对接文档
  10. 1438 绝对差不超过限制的最长连续子数组(暴力破解)
  11. 使用WarZone联机对战横扫千军(TA)指南
  12. 如何装配一台计算机和安装操作系统,组装机怎么安装系统?手把手教你组装机安装系统教程...
  13. 安卓AsyncTask
  14. Bugzilla的bug状态
  15. 计算机无法启用媒体流,win10电脑媒体流启动不了怎么办_win10电脑媒体流无法启动解决方法-win7之家...
  16. JavaSE —— 使用 Javac 编译 Java 程序
  17. php hiphop mysql_HipHop PHP实战(详解web运行模式)
  18. mysql数据库西里尔文乱码_如何将Window-1251(俄语西里尔字母)MySql数据库转换为UTF-8...
  19. 获取客户端IP地址定位城市信息
  20. 2020云栖大会智慧出行专场:聚焦高精地图/算法、智能模型、自动驾驶、AR导航

热门文章

  1. java中jsp怎么传递参数_急!Java问题,Java如何获得jsp传递的参数??
  2. 下行物理信道rs_这些CH结尾的LTE物理信道傻傻分不清楚?快来看这篇文章!
  3. 理论应用实例水杯_PLC应用,3个PID控制实例讲解,想不会都难!
  4. 搭建golang+vscode开发环境
  5. 详细记录一次npm i canvas报错的解决过程
  6. LINQ-to-SQL那点事~LINQ-to-SQL中的并发冲突与应对
  7. 南理工14级第4组软件课程设计报告
  8. Linux指令--touch
  9. 那些年做过的 .NET Web 项目和 iOS 之路的一些思考
  10. androidannotations 在android studio中的使用