【Python】Python 利用模块实现单例模式

Python 利用模块实现单例模式

在GOF的23种设计模式中,单例是最常使用的模式,通过单例模式可以保证系统中 一个类只有一个实例而且该实例易于被外界访问,从而方便对实例个数的控制并节约系统资 源。每当大家想要实现一个名为XxxMangcr的类时,往往意味着这是一个单例。在游戏编程 中尤是如此,比如一个名为World的单例管理着游戏中的所有资源,包括一个名为Sun的单 例,它给这个世界带来了光亮。

单例如此常见,所以有不少现代编程语言将其加到了语言特性中,如scala和falcon语 言都把object定义成关键同,并用其声明单例。如在scala中,---个单例如下:

object Singleton{

def show = println("I am a singleton")

}

object定义了一个名为Singleton的单例,它满足单例的3个需求:一是只能有一个实 例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例3对于第三点. 在任何地方都可以通过调用Singleton.show()来验证。在scala中,单例没有显式的初始化操 作.但并不是所有在语法层面支持单例模式的编程语言都如此,比如falcon就不一样。

object object_name from classl, class2 ... classN

property__l = expression

property_2 = expression

...

property_N = expression

init block

function method_l( parameter_list)

method_body

end

function method_N(lparameter_list])

method_body

end

end

上面是falcon语言的单例语法,init block能够让程序员手动控制单例的初始化代码。但是与scala和falcon相比,动态语言Python就没有那么方便了,主要原因是Python缺乏声 明私有构造函数的语法元素,实例又带有类型信息。比如以下方法是不可行的:

class _Singleton(object):

pass

Singleton = _Singleton()

del _Singleton # 试图刪除 class 定义

another = Singleton.class () # 没用,绕过!

print(type(another))

#输出

<class 'main ._Singleton'>

可见虽然把Singleton的类定义删除了,但仍然有办法通过己有实例的__class__属性生成一个新的实例。于是许多Pythonista把目光聚集到真正创建实例的方法__new__上,并做起了文章。
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

if name =="mian ":

s1=Singleton()

s2=Singleton()

assert id(s1) == id(s2)

这个方法很好地解决了前面的问题,现在基本上可以保证"只能有一个实例"的要求了, 但是在并发情况下可能会发生意。为了解决这个问题,引入一个带锁的版本。
import threading

class Singleton(object):

objs =()

objs_locker = threading.Lock()

def new (cls, *args, **kv):

if cls in cls.objs:

return cls.objscls

cls.objs_locker.acquire()

try:

if cls in cls.objs: ## double check locking

return cls.objscls

cls.objscls = object.new (cls)

finally:

cls.objs_locker.release()

利用经典的双检査锁机制,确保了在并发环境下Singleton的正确实现。但这个方案并 不完美,至少还有以下两个问题:

如果Singleton的子类重载了 new()方法,会覆盖或者干扰Singleton类中__new__()的执行,虽然这种情况出现的概率极小,但不可忽视。

如果子类有__init__()方法,那么每次实例化该Singleton的时候,init ()都会被调 用到,这显然是不应该的,init()只应该在创建实例的时候被调用一次。

这两个问题当然可以解决,比如通过文档告知其他程序员,子类化Singleton的时候, 请务必记得调用父类的__new__()方法;而第二个问题也可以通过偷偷地替换掉方 法来确保它只调用一次。但是,为了实现一个单例,做大量的、水面之下的工作让人感觉相 当不Pythonic。这也引起了 Python社区的反思,有人开始重新审视Python的语法元素,发 现模块采用的其实是天然的单例的实现方式。

所有的变量都会绑定到模块。

模块只初始化一次。

import机制是线程安全的(保证了在并发状态下模块也只有一个实例)。

当我们想要实现一个游戏世界时,只需简单地创建World.py就可以了。

World.py

import Sun

def run():

while True:

Sun.rise()

Sun.set()

然后在人口文件main.py里导入,并调用run()函数,看,是不是感觉这种方式最为 Pythonic 呢?

main.py

import World

World.run()

Alex Martelli认为单例模式要求"实例的唯一性"本身是有问题的,实际更值得关注 的是实例的状态,只要所有的实例共享状态(可以狭义地理解为属性)、行为(可以狭 义地理解为方法)一致就可以了。在这一思想的进一步指导下,他提出了 Borg模式 在C#中又称为Monostate模式)。

class Borg:

_shared_state = {}

def init (self):

self.diet = self.__shared_state

and whatever else you want in your class -- that's all!

通过Borg模式,可以创建任意数量的实例,但因为它们共享状态.从而保证了行为一致。虽然Alex的这个Borg模式仅适用于古典类(classic clasess), Python 2.2版本 以后的新式类(new-style classes)需要使用__getattr__和__setattr__方法来实现(代码略),但其可开阔眼界。

相关推荐
笨鸟先飞,勤能补拙5 小时前
AI 赋能网络安全:技术全景、成熟度评估与实战案例
人工智能·python·安全·web安全·网络安全·sqlite·github
长和信泰光伏储能6 小时前
京津冀光伏发电:绿色能源的未来之路
python·能源
浦信仿真大讲堂6 小时前
从重复操作到自动化闭环:如何让 CST 与 Python 真正协同起来
python·自动化·cst·仿真软件·达索软件
Gu Gu Study6 小时前
ScoutLoop开放域深度研究引擎(agent的初步设计想法)
人工智能·python
卷无止境7 小时前
写代码这件事,到底该讲究点什么?
后端·python
卷无止境7 小时前
循环复杂度到底在算什么,Python 代码怎么才能写得让人一看就懂
后端·python
lpfasd1237 小时前
MediaCrawler 项目深度分析
chrome·python·chrome devtools
Dxy12393102168 小时前
Python项目打包成EXE完整教程(PyInstaller实战避坑)
开发语言·python
bamb008 小时前
一个项目带你入门AI应用开发01
python
0566469 小时前
Python康复训练——常用标准库
开发语言·python·学习