很多初学者喜欢用全局变量,因为这比函数的参数传来传去更容易让人理解。确实在很多场景下用全局变量很方便。不过如果代码规模增大,并且有多个文件的时候,全局变量就会变得比较混乱。你可能不知道在哪个文件中定义了相同类型甚至重名的全局变量,也不知道这个变量在程序的某个地方被做了怎样的操作。

因此对于这种情况,有种更好的实现方式: 单例(Singleton)

单例是一种设计模式,应用该模式的类只会生成一个实例。

单例模式保证了在程序的不同位置都可以且仅可以取到同一个对象实例:如果实例不存在,会创建一个实例;如果已存在就会返回这个实例。因为单例是一个类,所以你也可以为其提供相应的操作方法,以便于对这个实例进行管理。

举个例子来说,比如你开发一款游戏软件,游戏中需要有“场景管理器”这样一种东西,用来管理游戏场景的切换、资源载入、网络连接等等任务。这个管理器需要有多种方法和属性,在代码中很多地方会被调用,且被调用的必须是同一个管理器,否则既容易产生冲突,也会浪费资源。这种情况下,单例模式就是一个很好的实现方法。

单例模式广泛应用于各种开发场景,对于开发者而言是必须掌握的知识点,同时在很多面试中,也是常见问题。本篇文章总结了目前主流的实现单例模式的方法供读者参考。

希望看过此文的同学,在以后被面到此问题时,能直接皮一下面试官,“我会 4 种单例模式实现,你想听哪一种?”

以下是实现方法索引:

  • 使用函数装饰器实现单例
  • 使用类装饰器实现单例
  • 使用 __new__ 关键字实现单例
  • 使用 metaclass 实现单例

使用函数装饰器实现单例

以下是实现代码:

def singleton(cls):
_instance = {} def inner():
if cls not in _instance:
_instance[cls] = cls()
return _instance[cls]
return inner @singleton
class Cls(object):
def __init__(self):
pass cls1 = Cls()
cls2 = Cls()
print(id(cls1) == id(cls2))

输出结果:

True

在 Python 中,id 关键字可用来查看对象在内存中的存放位置,这里 cls1 和 cls2 的 id 值相同,说明他们指向了同一个对象。

关于装饰器的知识,有不明白的,使用搜索引擎再学习一遍。代码中比较巧妙的一点是:

_instance = {}

使用不可变的类地址作为键,其实例作为值,每次创造实例时,首先查看该类是否存在实例,存在的话直接返回该实例即可,否则新建一个实例并存放在字典中。

使用类装饰器实现单例

代码:

class Singleton(object):
def __init__(self, cls):
self._cls = cls
self._instance = {}
def __call__(self):
if self._cls not in self._instance:
self._instance[self._cls] = self._cls()
return self._instance[self._cls] @Singleton
class Cls2(object):
def __init__(self):
pass cls1 = Cls2()
cls2 = Cls2()
print(id(cls1) == id(cls2))

同时,由于是面对对象的,这里还可以这么用

class Cls3():
pass Cls3 = Singleton(Cls3)
cls3 = Cls3()
cls4 = Cls3()
print(id(cls3) == id(cls4))

使用 类装饰器实现单例的原理和 函数装饰器 实现的原理相似,理解了上文,再理解这里应该不难。

New、Metaclass 关键字

在接着说另外两种方法之前,需要了解在 Python 中一个类和一个实例是通过哪些方法以怎样的顺序被创造的。

简单来说,元类(metaclass) 可以通过方法 __metaclass__ 创造了类(class),而类(class)通过方法 __new__ 创造了实例(instance)

在单例模式应用中,在创造类的过程中或者创造实例的过程中稍加控制达到最后产生的实例都是一个对象的目的。

本文主讲单例模式,所以对这个 topic 只会点到为止,有感兴趣的同学可以在网上搜索相关内容,几篇参考文章:

  • What are metaclasses in Python? https://stackoverflow.com/questions/100003/what-are-metaclasses-in-python
  • python-__new__-magic-method-explained http://howto.lintel.in/python-__new__-magic-method-explained/
  • Why is __init__() always called after __new__()? https://stackoverflow.com/questions/674304/why-is-init-always-called-after-new

使用 new 关键字实现单例模式

使用 __new__ 方法在创造实例时进行干预,达到实现单例模式的目的。

class Single(object):
_instance = None
def __new__(cls, *args, **kw):
if cls._instance is None:
cls._instance = object.__new__(cls, *args, **kw)
return cls._instance
def __init__(self):
pass single1 = Single()
single2 = Single()
print(id(single1) == id(single2))

在理解到 __new__ 的应用后,理解单例就不难了,这里使用了

_instance = None

来存放实例,如果 _instance 为 None,则新建实例,否则直接返回 _instance 存放的实例。

使用 metaclass 实现单例模式

同样,我们在类的创建时进行干预,从而达到实现单例的目的。

在实现单例之前,需要了解使用 type 创造类的方法,代码如下:

def func(self):
print("do sth") Klass = type("Klass", (), {"func": func}) c = Klass()
c.func()

以上,我们使用 type 创造了一个类出来。这里的知识是 mataclass 实现单例的基础。

class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls] class Cls4(metaclass=Singleton):
pass cls1 = Cls4()
cls2 = Cls4()
print(id(cls1) == id(cls2))

这里,我们将 metaclass 指向 Singleton 类,让 Singleton 中的 type 来创造新的 Cls4 实例。

Python单例模式(Singleton)的N种实现的更多相关文章

  1. OOAD之单例模式Singleton的6种写法

    1  主要作用是保证在Java应用程序中,一个类Class只有一个实例存在. 一 :第一种 饿汉式(预加载) public class Singleton { private Singleton(){ ...

  2. Python实现Singleton模式的几种方式

    使用python实现设计模式中的单例模式.单例模式是一种比较常用的设计模式,其实现和使用场景判定都是相对容易的.本文将简要介绍一下python中实现单例模式的几种常见方式和原理.一方面可以加深对pyt ...

  3. 二十四种设计模式:单例模式(Singleton Pattern)

    单例模式(Singleton Pattern) 介绍保证一个类仅有一个实例,并提供一个访问它的全局访问点. 示例保证一个类仅有一个实例. Singleton using System; using S ...

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

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

  5. 设计模式之单例模式——Singleton

                        设计模式之单例模式--Singleton 设计意图: 保证类仅有一个实例,并且可以供应用程序全局使用.为了保证这一点,就需要这个类自己创建自己的对象,并且对外有 ...

  6. 【白话设计模式四】单例模式(Singleton)

    转自:https://my.oschina.net/xianggao/blog/616385 0 系列目录 白话设计模式 工厂模式 单例模式 [白话设计模式一]简单工厂模式(Simple Factor ...

  7. ooad单例模式-Singleton

                                                单例模式Singleton 主要作用是保证在Java应用程序中,一个类Class只有一个实例存在. 比如建立目录 ...

  8. iOS单例模式(Singleton)写法简析

    单例模式的意思就是只有一个实例.单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例.这个类称为单例类. 1.单例模式的要点: 显然单例模式的要点有三个:一是某个类只能有一个实例: ...

  9. 浅谈设计模式--单例模式(Singleton Pattern)

    题外话:好久没写blog,做知识归纳整理了.本来设计模式就是个坑,各种文章也写烂了.不过,不是自己写的东西,缺少点知识的存在感.目前还没做到光看即能记住,得写.所以准备跳入设计模式这个大坑. 开篇先贡 ...

随机推荐

  1. javascript一些实用的方法

    判断数据类型 function isType(type) { return function(obj) { return {}.toString.call(obj) == "[object ...

  2. 【编程开发】opencv实现对Mat中某一列或某一行的元素进行normalization

    [编程开发]opencv实现对Mat中某一列或某一行的元素进行normalization 标签: [编程开发] [机器学习] 声明:引用请注明出处http://blog.csdn.net/lg1259 ...

  3. [学习笔记] Hibernate 4.3.5 下载与安装

    下载 http://hibernate.org/orm/releases/4.3/ https://sourceforge.net/projects/hibernate/files/hibernate ...

  4. Python+request超时和重试

    Python+request超时和重试 一.什么是超时? 1.连接超时 连接超时指的是没连接上,超过指定的时间内都没有连接上,这就是连接超时.(连接时间就是httpclient发送请求的地方开始到连接 ...

  5. dede按照权重排序dede:list和得的:arclist

    dede:list 的方法 1.找到"根目录\include\arc.listview.class.php"文件. 2.修改代码:在文件第727行处添加按weight排序判断代码( ...

  6. fiddler笔记:状态面板

    Capturing 指示Fiddler是否开启抓包. Process-based Filter 显示Fiddler当前正在捕获的流量进程类型点击面板可以显示进程类型的过滤选项菜单 Automatic ...

  7. Python 【Debug排除程序故障】

    debug #排除程序故障 print()函数常和#号注释结合在一起用来debug 多行注释有两种快捷操作:1.在需要注释的多行代码块前后加一组三引号''' 2.选中代码后使用快捷键操作:Window ...

  8. 优化方法总结以及Adam存在的问题(SGD, Momentum, AdaDelta, Adam, AdamW,LazyAdam)

    优化方法总结以及Adam存在的问题(SGD, Momentum, AdaDelta, Adam, AdamW,LazyAdam) 2019年05月29日 01:07:50 糖葫芦君 阅读数 455更多 ...

  9. Faster RCNN学习笔记

    感谢知乎大神的分享 https://zhuanlan.zhihu.com/p/31426458 Ross B. Girshick在2016年提出了新的Faster RCNN,在结构上,Faster R ...

  10. css优先级及其对应的权重

    1.选择器的优先级 !important>内联选择器(style)>id选择器>类选择器 | 属性选择器 | 伪类选择器 > 元素选择器>通配符(*) 2.选择器的权重( ...