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

因此对于这种情况,有种更好的实现方式: 单例(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. DB2分页

    前言 最近在做一个DB2的项目,遇到分页处理的设计时开始犯难.以前一直采用MySQL作为项目数据库,其中的Limit关键字非常人性化,MySQL把分页的处理逻辑封装到了数据库的核心中,使得做查询设计时 ...

  2. 【编程开发】MD5算法原理

    MD5(单向散列算法)的全称是Message-Digest Algorithm 5(信息-摘要算法),经MD2.MD3和MD4发展而来.MD5算法的使用不需要支付任何版权费用.     MD5功能: ...

  3. VS编译错误._CRT_SECURE_NO_WARNINGS、_WINSOCK_DEPRECATED_NO_WARNINGS

    1.不记得原来的情况了,记得大概是这样: 低版本的 VC编译器 使用 strcpy.sprintf 等它不会报错,但是 高版本的 VS编译就会报错,大意是 strcpy.sprintf 等函数 不安全 ...

  4. 使用webpack搭建一个多页应用

     一.前言 最近需要为公司的活动写8个左右的移动端分享页面,有比较多的页面是公用的,如果用传统的方式来写的话,对于公用的代码抽取,css代码的压缩都是比较麻烦的,所以选择了webpack来搭建一个基本 ...

  5. 学习pandas apply方法,看这一篇就够了,你该这么学,No.10

    最近好忙啊,好忙啊,忙的写不动博客了 时间过得飞快 一晃,一周就过去了 本着不进步就倒退的性格 我成功的在技术上面划水了一周 今天要学习的还是groupby的高级进阶 说是高级,其实就是比初级复杂了一 ...

  6. Android 变量取名神器

    前言 在工作中,我们还在为起变量名而苦恼吗?今天无意间发现一个专门为变量取名而诞生的神器 codelf. 我们可以直接浏览器访问 http://unbug.github.io/codelf/ 现在我们 ...

  7. svn clean up

    1.下载sqlite3.exe 2.找到你项目的.svn文件,查看是否存在wc.db3.将sqlite3.exe放到.svn的同级目录4.启动cmd执行sqlite3 .svn/wc.db " ...

  8. Shortest Unsorted Continuous Subarray

    Given an integer array, you need to find one continuous subarray that if you only sort this subarray ...

  9. HDU 3047 带权并查集 入门题

    Zjnu Stadium 题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=3047 Problem Description In 12th Zhejian ...

  10. LeetCode 答案(python)1-17

    1.给定一个整数数组和一个目标值,找出数组中和为目标值的两个数. 你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用. 示例: 给定 nums = [2, 7, 11, 15], targe ...