Python 如何实现单例模式?请写出至少两种实现方法?
单例模式(Singleton Pattern) 是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。
比如,某个服务器程序的配置信息存放在一个文件中,客户端通过一个 AppConfig 的类来读取配置文件的信息。如果在程序运行期间,有很多地方都需要使用配置文件的内容,也就是说,很多地方都需要创建 AppConfig 对象的实例,这就导致系统中存在多个 AppConfig 的实例对象,而这样会严重浪费内存资源,尤其是在配置文件内容很多的情况下。
事实上,类似 AppConfig 这样的类,我们希望在程序运行期间只存在一个实例对象。
在 Python 中,我们可以用多种方法来实现单例模式:
1.使用模块; 2. 使 用 new ; 3.使用装饰器; 4. 使用元类(metaclass)。
(1)使用模块:
其实,Python 的模块就是天然的单例模式,因为模块在第一次导入时,会生成.pyc 文件,当第二次导入时,就会直接加载.pyc 文件,而不会再次执行模块代码。因此我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。
1.# mysingle.py
2.class MySingle:
3.def foo(self):
4.pass
5.
6. sinleton = MySingle()
7.
8.将上面的代码保存在文件 mysingle.py 中,然后这样使用:
9.from mysingle import sinleton
10.singleton.foo()
(2)使用 new :
为了使类只能出现一个实例,我们可以使用 new 来控制实例的创建过程,
class Singleton(object):
def new (cls):
# 关键在于这,每一次实例化的时候,我们都只会返回这同一个 instance 对象
if not hasattr(cls, 'instance'):
cls.instance = super(Singleton, cls). new (cls)
return cls.instance
obj1 = Singleton()
obj2 = Singleton()
obj1.attr1 = 'value1'
print obj1 is obj2
输出结果:
value1 value1
(3)使用装饰器:
装饰器可以动态的修改一个类或函数的功能。这里,我们也可以使用装饰器来装饰某个类,使其只能生成一个实例
def singleton(cls):
instances = {}
def getinstance(*args,**kwargs):
if cls not in instances:
instances[cls] = cls(*args,**kwargs)
return instances[cls]
return getinstance
@singleton
class MyClass:
a = 1
c1 = MyClass()
c2 = MyClass()
print(c1 == c2) # True
在上面,我们定义了一个装饰器 singleton,它返回了一个内部函数 getinstance,
该函数会判断某个类是否在字典 instances 中,如果不存在,则会将 cls 作为 key,cls(*args, **kw) 作为 value 存到instances 中 ,否则,直接返回 instances[cls]。
(4)使用 metaclass(元类):
元类可以控制类的创建过程,它主要做三件事:
- 拦截类的创建
- 修改类的定义
- 返回修改后的类
class Singleton2(type):
def init (self, *args, **kwargs):
self. instance = None
super(Singleton2,self). init (*args, **kwargs)
def call (self, *args, **kwargs):
if self. instance is None:
self. instance = super(Singleton2,self). call (*args, **kwargs)
return self. instance
class Foo(object):
__metaclass = Singleton2 #在代码执行到这里的时候,元类中的 new 方法和 init 方法其实已经被执行了,而不是在 Foo 实例化的时候执行。且仅会执行一次。
foo1 = Foo()
foo2 = Foo()
print (Foo. dict ) #_Singleton instance': < main .Foo object at 0x100c52f10> 存在一个私有属性来保存属性, 而不会污染 Foo 类(其实还是会污染,只是无法直接通过 instance 属性访问)
print (foo1 is foo2) # True
单例模式优缺点
优点:
-
实例控制:单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。
-
灵活性:因为类控制了实例化过程,所以类可以灵活更改实例化过程。
缺点:
-
开销:虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题。
-
可能的开发混淆:使用单例对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用 new 关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类。
-
对象生成期:不能解决删除单个对象的问题。在提供内存管理的语言中(例如基于 .NET Framework 的语言),只有单例类能够导致单例类中出现悬浮引用。