Python -笔记 -单例模式(Singleton)实现


本文适用对象:

python正在进阶的人儿~
高级 Python 语法,涉及装饰器


不管是编写类还是函数都是变量之间的传来传去,最简单粗暴的方式就是全局变量。确实在很多场景下用全局变量很方便。不过如果代码规模增大,并且有多个文件的时候,全局变量就会变得比较混乱。你可能不知道在哪个文件中定义了相同类型甚至重名的全局变量,造成悲剧。

这里介绍一种设计模式,单例模式

What

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

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

Why

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

How

怎么实现单例设计模式?
下面提供3种实现方法:

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

使用函数装饰器实现单例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def singleton(cls):
_instance = {}

def inner():
if cls not in _instance:
_instance[cls] = cls()
return _instance[cls]

return inner

@singleton
class Cla(object):
def __init__(self):
pass

cls1 = Cla()
cls2 = Cla()
print(id(cls1) == id(cls2))

输出结果:

1
True

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

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

使用类装饰器实现单例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Singleton(object):
def __init__(self, cls):
self._cls = cls
self._instance = {}
def __call__(self, *args, **kwargs):
if self._cls not in self._instance:
self._instance[self._cls] = self._cls()
return self._instance[self._cls]

@Singleton
class Cla1(object):
def __init__(self):
pass

cls1 = Cla1()
cls2 = Cla1()
print(id(cls1)==id(cls2))

使用 类装饰器实现单例的原理和 函数装饰器 实现的原理相似
----------------------
补充:装饰器
简单地说:他们是修改其他函数的功能的函数
他们有助于让我们的代码更简短,也更Pythonic(Python范儿)

首先在理解装饰器之前我们要先理解,从函数中返回函数将函数作为参数传给另一个函数这两个概念。
1.从函数中返回函数
举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def hi(name="yasoob"):
def greet():
return "now you are in the greet() function"

def welcome():
return "now you are in the welcome() function"

if name == "yasoob":
return greet
else:
return welcome

a = hi()
print(a)

#outputs: <function hi.<locals>.greet at 0x000000B36FE96488>

#上面清晰地展示了`a`现在指向到hi()函数中的greet()函数
#现在试试这个
#outputs: now you are in the greet() function

再次看看这个代码。在 if/else 语句中我们返回 greet 和 welcome,而不是 greet() 和 welcome()。为什么那样?这是因为当你把一对小括号放在后面,这个函数就会执行;
然而如果你不放括号在它后面,那它可以被到处传递,并且可以赋值给别的变量而不去执行它。
当我们写下 a = hi(),hi() 会被执行,而由于 name 参数默认是 yasoob,所以函数 greet 被返回了。如果我们把语句改为 a = hi(name = “ali”),那么 welcome 函数将被返回。我们还可以打印出 hi()(),这会输出 now you are in the greet() function。

2.将函数作为参数传给另一个函数

1
2
3
4
5
6
7
8
def hi():
print('Hello Big data industry!!!!!')

def dosomethingBeforehi(func):
print("I am echo!!!!!!")
func()

dosomethingBeforehi(hi)

现在你已经具备所有必需知识,来进一步学习装饰器真正是什么了。装饰器让你在一个函数的前后去执行代码。
你的第一个装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def a_new_decorator(a_func):

def wrapTheFunction():
print("I am doing some boring work before executing a_func()")

a_func()

print("I am doing some boring work after executing a_func()")

return wrapTheFunction

def a_function_requiring_decoration():
print("I am the function which needs some decoration to remove my foul smell")

a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)
#now a_function_requiring_decoration is wrapped by wrapTheFunction()

a_function_requiring_decoration()
#outputs:I am doing some boring work before executing a_func()
# I am the function which needs some decoration to remove my foul smell
# I am doing some boring work after executing a_func()

现在你也许疑惑,我们在代码里并没有使用 @ 符号?那只是一个简短的方式来生成一个被装饰的函数。这里是我们如何使用 @ 来运行之前的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def a_new_decorator(a_func):
def wrapTheFunction():
print("I am doing some boring work before executing a_func()")

a_func()

print("I am doing some boring work after executing a_func()")

return wrapTheFunction
@a_new_decorator
def a_function_requiring_decoration():
"""Hey you! Decorate me!"""
print("I am the function which needs some decoration to "
"remove my foul smell")

a_function_requiring_decoration()
# outputs: I am doing some boring work before executing a_func()
# I am the function which needs some decoration to remove my foul smell
# I am doing some boring work after executing a_func()

蓝本规范:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from functools import wraps
def decorator_name(f):
@wraps(f)
def decorated(*args, **kwargs):
if not can_run:
return "Function will not run"
return f(*args, **kwargs)
return decorated

@decorator_name
def func():
return("Function is running")

can_run = True
print(func())
# Output: Function is running

can_run = False
print(func())
# Output: Function will not run

注意:@wraps接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等的功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性

----------------------

使用 new 关键字实现单例

在说这个实现之前,首先我们需要知道Python 中一个类和一个实例是通过哪些方法以怎样的顺序被创造的。

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

元类可以理解成类的爸爸,在python世界,拥有一个永恒的道,那就是“type”。type就是道。如此广袤无垠的python生态圈,都是由type产生出来的。在这里有一句很太极生两仪,两仪生四象,四象生八卦的话帮助理解python生态圈~

道生一,一生二,二生三,三生万物。
-道 即是 type
-一 即是 metaclass(元类,或者叫类生成器)
-二 即是 class(类,或者叫实例生成器)
-三 即是 instance(实例)
-万物 即是 实例的各种属性与方法,我们平常使用python时,调用的就是它们。

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

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

1
2
3
4
5
6
7
8
9
10
11
12
class Singleton(object):
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = object.__new__(cls)
return cls._instance
def __init__(self):
pass

single = Singleton()
single2 = Singleton()
print(id(single)==id(single2))

总结:

1.什么是单例模式?
单例是一种设计模式,应用该模式的类只会生成一个实例。
2.为什么要用单例模式?
举例:一款游戏软件只能有一个控制面板
3.怎么实现单例模式?
 函数装饰器实现单例
 类装饰器实现单例
 new 关键字实现单例


参考:
Python单例模式(Singleton)的N种实现
两句话掌握 Python 最难知识点——元类
Python 函数装饰器



觉得不错的话,给点打赏吧 ୧(๑•̀⌄•́๑)૭



wechat pay



alipay

Python -笔记 -单例模式(Singleton)实现
http://yuting0907.github.io/2019/04/13/Python -笔记 -单例模式(Singleton)实现/
作者
yuting
发布于
2019年4月13日
许可协议