前言

单例模式是设计模式(Design Pattern)中最简单、最容易理解的一种,维基百科[1]的定义如下:

单例模式,也叫单子模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的类 "类 (计算机科学)")必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。

单例模式的主要优点是共享资源和减少资源消耗,主要应用于IO或数据库的线程池,缓存,日志,对话和需共享数据的资源等,但是在实现情况中滥用单例模式会带来很多意想不到的问题,本文重点在于介绍几种Python实现单例模式的方法,这里就不再展开论述了。文中所演示的代码都会托管在Github上。

简单实现

首先,我们先尝试用Python内部类(嵌套类)来实现单例模式:

#coding=utf-8

class Singleton:

"""单列类

"""

class __MyClass:

"""实际生成实例的类

"""

def __init__(self, arg):

"""初始化并赋值"""

self.foo = arg

def display(self):

"""返回实例的id和属性值"""

return (id(self), self.foo)

# 类属性

_instance = None

def __init__(self, arg):

if not Singleton._instance:

Singleton._instance = Singleton.__MyClass(arg)

else:

Singleton._instance.foo = arg

def __getattr__(self, attr):

return getattr(self._instance, attr)

注意实际生成实例的类是内部的“__MyClass”类,前面的双下划线代表这是一个私有的类,用户不能再外面直接访问它。而在"__MyClass"类外封装了一个“Singleton”类,这个类的任务就是在初始化时保证整个上下文中只有一个实例,实现的方式很简单。用一个私有属性__instance_保存当前生成的实例,在初始化时判断实例是否为_None_,如果是就用“__MyClass”类生成一个新实例并赋值给__instance_,否就直接返回或调用当前__instance_的实例。最后用"__MyClass"里的实现的方法测试一下:

if __name__ == "__main__":

"""测试"""

s1 = Singleton("bar")

s2 = Singleton("zoo")

print(s1.display())

print(s2.display())

# output

>(41706760L, 'zoo')

>(41706760L, 'zoo')

基类

现在我们考虑将inner class拆分出来,因为在Python类实例化时会调用___new___方法[2]来生成实例,所以我们可以先继承“Singleton”类,然后通过重写基类的___new___方法让其实现单例模式:

#coding=utf-8

class Singleton(object):

"""单例类

"""

_instance = None

def __new__(cls, *args, **kwargs):

if not cls._instance:

cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)

return cls._instance

class MyClass(Singleton):

"""实际生成实例的类

"""

def __init__(self, arg):

self.foo = arg

def display(self):

return (id(self), self.foo)

测试结果:

if __name__ == "__main__":

s1 = MyClass("bar")

s2 = MyClass("zoo")

print(s1.display())

print(s2.display())

assert s1 is s2

# output

>(40882416L, 'zoo')

>(40882416L, 'zoo')

装饰器

第三种就是最常见的用装饰器来实现单列模式:

#coding=utf-8

def singleton(cls):

instances = {}

def wrapper(*args, **kwargs):

if cls not in instances:

instances[cls] = cls(*args, **kwargs)

return instances[cls]

return wrapper

@singleton

class MyClass:

"""实际生成实例的类

"""

foo = "foo"

def display(self):

return (id(self))

@singleton

class OtherClass:

"""另一个类

"""

pass

装饰器的实现过程是将生成的实例都放到一个名为_instances_的Dict中映射好,这样每次在类初始化时先检查_instances_中是否已经包含有例化好的实例,有就直接返回是咧,没有则调用类初始化一个并赋值给_instances_列表。装饰器的好处在于用一个Dict列表来管理所有需要实现单例模式的类,更简便和通用化。代码的测试结果如下:

if __name__ == "__main__":

s1 = MyClass()

s1.foo = "bar"

print(s1.display(), s1.foo)

s2 = MyClass()

s2.foo = "zoo"

print(s2.display(), s2.foo)

assert s1 is s2

s3 = OtherClass()

s4 = OtherClass()

assert s3 is s4

元类

如果希望不仅仅是通过限制而是在源头上就创建一个单例类,我们需要用到元类来实现,元类可以参考Stackoverflow[3]上的一个解答。简单的说就是Python中的类也是一种对象,被称为类对象。类对象可以通过元类type来创建,而在此过程中会调用type的 __call__ 方法。所以我们只要在type创建类对象的过程中重写 __call__ 方法,在其中加入相应的创建单例的逻辑即可实现单例模式,具体代码实现如下:

#coding=utf-8

class Singleton(type):

def __call__(cls, *args, **kwargs):

"""重写,实现单例模式"""

if not hasattr(cls, '_instance'):

cls._instance = super(Singleton, cls).__call__(*args, **kwargs)

return cls._instance

class MyClass(object):

# 指定元类

__metaclass__ = Singleton

def display(self):

return (id(self))

代码的测试与前面类似,这里就不再累述了。

线程安全

最后,需要注意的是单例模式在多线程下可能会出现线程安全的问题,这时候就需要在单例的初始化过程中加上线程同步锁来避免,但这样又会降低整体的性能,具体可以参考这篇文档。

参考

python如何实现单例模式_用Python实现设计模式——单例模式相关推荐

  1. python大牛 关东升_《Python从小白到大牛》第4章 Python语法基础

    本章主要为大家介绍Python的一些语法,其中包括标识符.关键字.常量.变量.表达式.语句.注释.模块和包等内容. 标识符和关键字 任何一种计算机语言都离不开标识符和关键字,因此下面将详细介绍Pyth ...

  2. python之禅 中文_《Python之禅》中对于Python编程过程中的一些建议

    <Python之禅>中对于Python编程过程中的一些建议 来源:中文源码网    浏览: 次    日期:2018年9月2日 [下载文档:  <Python之禅>中对于Pyt ...

  3. python画交互式地图_使用Python构建交互式地图-入门指南

    python画交互式地图 Welcome to The Beginner's Guide to Building Interactive Maps in Python 欢迎使用Python构建交互式地 ...

  4. python出现的意义_[转]Python中下划线以及命名空间的意义

    Python 用下划线作为变量前缀和后缀指定特殊变量/方法. 主要存在四种情形 1. 1. object # public 2. __object__ # special, python system ...

  5. python剪辑视频 裁剪_用python进行视频剪辑

    一.目标 python,利用moviepy和pydub将一段视频进行区间切割 二.源码 import os from moviepy.video.io.VideoFileClip import Vid ...

  6. python大数据免费_用python做大数据

    不学Python迟早会被淘汰?Python真有这么好的前景? 最近几年Python编程语言在国内引起不小的轰动,有超越Java之势,本来在美国这个编程语言就是最火的,应用的非常非常的广泛,而Pytho ...

  7. python字符串去掉空行_从python中的字符串中删除空格

    python字符串去掉空行 如何在python中删除字符串中的空格 (How to remove whitespaces in a string in python) str.lstrip()str. ...

  8. 用python做自我介绍_用python做个自我介绍(python入门教程)_逻辑教育

    原标题:用python做个自我介绍(python入门教程)_逻辑教育 本文涉及的python基础语法为:数据类型等 数字类型 1. 字符串的拼接 我们在上一章中已经简单介绍了一下字符串的创建方式,这里 ...

  9. python delimiter分隔符用法_使用Python文件读写,自定义分隔符(custom delimiter)

    众所周知,python文件读取文件的时候所支持的newlines(即换行符),是指定的.这一点不管是从python的doucuments上还是在python的源码中(作者是参考了python的io版本 ...

  10. python中级项目下载_中级Python复习:教程,项目思想和技巧

    python中级项目下载 本文旨在向Python初学者和开发人员介绍Python中使用的一些关键概念,这些概念一开始就没有讲授. 如果您可以创建二次方根求解器,则可以理解本文. 这些是我一天之内没有学 ...

最新文章

  1. 2009-2019年全国大学生智能汽车竞赛获奖数据分析
  2. 低端没出路,请接触高端!
  3. linux下用户配置文件与系统配置文件
  4. 中国镍氢电池行业产销状况及竞争格局咨询报告2021-2027年版
  5. Python的scrapy框架POST方式爬虫时碰见__VIEWSTATE和__EVENTVALIDATION的参数处理
  6. 有点难度,几道和「滑动窗口」有关的算法面试题
  7. 分析现有 WPF / Windows Forms 程序能否顺利迁移到 .NET Core 3.0
  8. Android之给gridview的单元格加上分割线
  9. 使用IDM下载,不适用默认浏览器下载
  10. unsafe jdk9_JDK 9清单:Project Jigsaw,sun.misc.Unsafe,G1,REPL等
  11. Java基础入门笔记-布尔类型变量
  12. openVINO2021.4安装记录
  13. 31. 了解各种与排序有关的选择
  14. oracle用imp导入dmp文件
  15. HCIE学习笔记(2)之ISIS Overload
  16. 5G移动通信网的定位技术发展趋势
  17. 三角形面积的计算公式,外接圆半径
  18. 金蝶KIS软件操作小技巧
  19. Asp.net的CheckBox控件和CheckBoxList控件
  20. linux系统内存dump机制介绍(一)--kdump

热门文章

  1. 新手引导 自定义遮罩 点击穿透
  2. php vm_facebook hiphop php vm 兑现概述(二)
  3. spring cloud config 加密配置
  4. Android计算器(仿小米计算器)
  5. Mybaits整个Spring项目,简单示例,10分钟快速上手
  6. Android游戏SQL注入,关于Android contentprovider sql注入问题
  7. cnn程序流程图_GitHub - suqcnn/vue: vue源码逐行注释分析+40多m的vue源码程序流程图思维导图 (diff部分待后续更新)...
  8. python sqlserver 数据操作_python上手--python操作数据库
  9. 图邻接表拓扑排序算法c语言完整,在用邻接表表示图时,拓扑排序算法时间复杂度为()...
  10. File.WriteAllText 写入TXT文件时不能正确换行只显示方块