单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。
单例模式的要点有三个;一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。
在 Python 中,我们可以用多种方法来实现单例模式:
使用模块
使用 new
使用装饰器(decorator)
使用元类(metaclass)
1.使用模块
如果我们真的想要一个单例类,可以考虑这样做:
#tests1.py
class MyClass(object):
def foo(self):
print(‘MyClass.foo’)
my_class_obj=MyClass()
将上面的代码保存在文件 tests1.py 中,然后这样使用:
from .tests1 import my_class_obj
my_class_obj.foo()
这种方法相当于把一个类写成一个实例化模块,无论你什么时候调用,都是那一个实例,所以就算一个单列了
2.使用__new__函数
为了使类只能出现一个实例,我们可以使用 new 来控制实例的创建过程,代码如下:
1
2
3
4
5
6
7
8
9
class MyClass(object):
_instance = None
def new(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(MyClass, cls).new(cls, *args, **kwargs)
return cls._instance
class HerClass(MyClass):
a = 1
在上面的代码中,我们将类的实例和一个类变量 _instance 关联起来,如果 cls._instance 为 None 则创建实例,否则直接返回 cls._instance。
执行情况如下:=
one = HerClass()
two = HerClass()
print(one == two) #True
print(one is two) #True
print(id(one), id(two)) #42818864 42818864
3. 使用装饰器
我们知道,装饰器(decorator)可以动态地修改一个类或函数的功能。这里,我们也可以使用装饰器来装饰某个类,使其只能生成一个实例,代码如下:
from functools import wraps
def singleton(cls):
instances = {}
@wraps(cls)
def getinstance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return getinstance
@singleton
class MyClass(object):
a = 1
在上面,我们定义了一个装饰器 singleton,它返回了一个内部函数 getinstance,该函数会判断某个类是否在字典 instances 中,如果不存在,则会将 cls 作为 key,cls(*args, **kw) 作为 value 存到 instances 中,否则,直接返回 instances[cls]。
4. 使用 metaclass
元类(metaclass)可以控制类的创建过程,它主要做三件事:
1.拦截类的创建
2.修改类的定义
3.返回修改后的类
使用元类实现单例模式的代码如下:
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 MyClass(metaclass=Singleton):
pass
优点:
一、实例控制
单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。
二、灵活性
因为类控制了实例化过程,所以类可以灵活更改实例化过程。
缺点:
一、开销
虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题。
二、可能的开发混淆
使用单例对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用 new关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类。
三、对象生存期
不能解决删除单个对象的问题。在提供内存管理的语言中(例如基于.NET Framework的语言),只有单例类能够导致实例被取消分配,因为它包含对该实例的私有引用。在某些语言中(如 C++),其他类可以删除对象实例,但这样会导致单例类中出现悬浮引用。
类也是对象
只要你使用关键字class,Python解释器在执行的时候就会创建一个对象。
下面的代码段:
class MyClass(object):
pass
print(MyClass) # 你可以打印一个类,因为它其实也是一个对象
#<class ‘main.MyClass’>
def echo(o):
print(o)
echo(MyClass) # 你可以将类做为参数传给函数
<class ‘main.MyClass’>
MyClass.new_attribute = ‘foo’ # 你可以为类增加属性
print(hasattr(MyClass,‘new_attribute’)) #True
print(MyClass.new_attribute) #foo
MyClassMirror=MyClass # 你可以将类赋值给一个变量
print(MyClassMirror())
<main.MyClass object at 0x00000000028CDE10>
动态地创建类
因为类也是对象,你可以在运行时动态的创建它们,就像其他任何对象一样。首先,你可以在函数中创建类,使用class关键字即可
def choose_class(name):
if name == ‘foo’:
class Foo(object):
pass
return Foo # 返回的是类,不是类的实例
else:
class Bar(object):
pass
return Bar
MyClass = choose_class(‘foo’)
print(MyClass) # 函数返回的是类,不是类的实例
<class ‘main.choose_class..Foo’>
print(MyClass()) # 你可以通过这个类创建类实例,也就是对象
<main.choose_class..Foo object at 0x00000000021E5CF8>
但这还不够动态。
由于类也是对象,所以它们必须是通过什么东西来生成的才对。
还记得内建函数type吗?这个古老但强大的函数能够让你知道一个对象的类型是什么,就像这样:
print(type(1)) #<class ‘int’>
print(type(‘1’)) #<class ‘str’>
print(type(MyClass)) #<class ‘type’>
print(type(MyClass())) #<class ‘main.MyClass’>