Python单例模式
作者:vergil 发布时间:April 9, 2017 分类:python
Singleton(单例) —— 对象创建型模式
在GoF的《设计模式:可复用面向对象软件的基础》如下定义:
意图
保证一个类仅有一个实例,并且提供一个访问它的全局访问点。
适用性
在下面的情况下可以使用Singleton模式
- 当类只能有一个实例切客户可以从一个众所周知的访问点访问它时。
- 当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。
参与者
Singleton —— 一定一个Instance操作,允许客户访问它的唯一实例。Instance是一个类操作(即Smalltalk中的一个类方法和C++中的一个静态成员函数)。
效果
对唯一实例的受控访问
缩小命名空间
Singleton模式是对全局变量的一种改进。它避免了那些存储唯一实例的全局变量污染命名空间允许对操作和表示的精化
Singleton类可以有子类。而且用这个扩展类的实例来配置一个应用是很容易的。你可以用你所需要的类的实例在运行时刻配置应用。允许可变数目的实例
这个模式使得你易于改变你的想法,并允许Singleton类的多个实例。此外,可以用相同的方法来控制应用所使用的实例的数目。只有允许访问Singleton实例的操作需要改变。比类操作更灵活
php实现
在一些语言(如C++,Java,php)实现单例模式基本上大同小异,私有构造方法使得无法在外部实例化单例对象,然后提供一个静态方法获得单例。以php为例:
<?php
class Singleton
{
// 私有静态属性,用于保存实例
private static $instance = null;
//私有构造方法,不允许在外部使用
private function __construct()
{
// ...
}
//提供一个访问它的静态方法获取实例
public static function getInstance()
{
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
//不允许克隆对象
private function __clone() {}
//不允许serialize
private function __sleep() {}
//不允许unserialize
private function __wakeup() {}
}
Python实现
然而python并没有真正的私有化支持。
__new__和 __init__
参考了python官方文档:https://docs.python.org/2/reference/datamodel.html#object.__new__
object.__new__(cls[, ...])
:创建一个新的类实例cls
时被调用。__new__()
是一个静态方法。它将请求实例的类作为其第一个参数。其余的参数是传递给对象构造方法表达式(对类的调用)。__new__()
的返回值应该是新的对象实例(通常是cls
的一个实例)。object.__init__(self[, ...])
:在实例被创建(由__new__()
)之后调用,但在它返回给调用者之前。参数是传递给类构造函数表达式的参数。如果基类有__init__()
方法,派生类的__init__()
方法(如果有的话),必须明确地调用它来确保实例的基类部分的正确初始化。例如:BaseClass.__init__(self, [args...])
。- 因为
__new__()
和__init__()
共同创建对象。(__new__()
用于创建,__init__()
用于定制),__init__()
不能返回非空值。这样做会导致在运行时引发TypeError。
使用__new__返回同一对象
class Singleton(object):
__instance = None
def __new__(cls, *args, **kwargs):
if cls.__instance is None:
cls.__instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
return Singleton.__instance
测试:
a = Singleton()
b = Singleton()
print id(a)
#4337124816
print id(b)
#4337124816
print a is b
#True
这看起来没什么问题。然而如上面所说__new__()
和__init__()
共同创建对象。虽然重写了__new__()
实现单例,但是__init__()
同样会被调用:
class Singleton(object):
__instance = None
def __init__(self):
print 'dosomething'
def __new__(cls, *args, **kwargs):
if cls.__instance is None:
cls.__instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
return Singleton.__instance
结果:
a = Singleton()
#dosomething
b = Singleton()
#dosomething
当然,可以稍作修改,不定义自动调用的__init__()
方法,实例化后手动调用自己写的init()
方法即可:
class Singleton(object):
__instance = None
def init(self):
print 'dosomething'
def __new__(cls, *args, **kwargs):
if cls.__instance is None:
cls.__instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
cls.__instance.init(*args, **kwargs)
return Singleton.__instance
使用装饰器
使用Python的装饰器可以轻松实现单例模式,而且非常自由,复用性高。
这个也是PEP-0318上的示例:
def singleton(cls):
#定义一个字典局部变量
instances = {}
#闭包函数,可以使用这个局部变量instances
def getinstance():
if cls not in instances:
instances[cls] = cls()
return instances[cls]
#返回闭包
return getinstance
@singleton #相当于singleton(MyClass)
class MyClass:
def __init__(self):
print 'dosomething'
测试:
a = MyClass()
#dosomething 只输出一次
b = MyClass()
print id(a)
#4337108536
print id(b)
#4337108536
print a is b
#True
像“其他语言”一样的单例模式
就是如本文开头所说,像php,java之类的语言的单例模式。不能直接实例化对象,通过公开的静态方法获取对象唯一实例。
class Singleton:
def __init__(self, decorated):
self._decorated = decorated
def Instance(self, *args, **kwargs):
"""
返回单例实例。在它的第一次调用时,它创建一个装饰器类的新实例,并调用'__init__()'方法。
在所有后续调用中,返回已创建的实例。
"""
try:
return self._instance
except AttributeError:
self._instance = self._decorated(*args, **kwargs)
return self._instance
# __call__()方法会在调用装饰器类时触发
# 例如,如果有一个Bar类,并没有使用这个装饰器类。
# 就会在调用 Singleton(Bar)() 时触发
# 如果使用了这个装饰器,如Foo。相当于不能直接调用 Foo() 实例化对象。
def __call__(self):
raise TypeError('Singletons must be accessed through `Instance()`.')
def __instancecheck__(self, inst):
return isinstance(inst, self._decorated)
@Singleton #相当于Singleton(Foo)
class Foo:
def __init__(self):
print 'Foo created'
调用代码:
f = Foo()
# 错误。TypeError: Singletons must be accessed through `Instance()`.
f = Foo.Instance()
g = Foo.Instance()
print f is g
# True
metaclass
以后补充
class Singleton(type):
def __init__(cls, name, bases, dict):
super(Singleton, cls).__init__(name, bases, dict)
cls.instance = None
def __call__(cls,*args,**kw):
if cls.instance is None:
cls.instance = super(Singleton, cls).__call__(*args, **kw)
return cls.instance
class MyClass(object):
__metaclass__ = Singleton
Borg design pattern
这个并不算真正意义上的单例模式,只是让所有实例共享属性。它直接把__dict__
指向同一个字典。
参考文章:http://code.activestate.com/recipes/66531/
class Borg:
__shared_state = {}
def __init__(self):
self.__dict__ = self.__shared_state
# and whatever else you want in your class -- that's all!
测试
a = MyClass()
b = MyClass()
print id(a)
#4337108464
print id(b)
#4337108536
print a is b
#False
a.name = 'Vergil' #设置a的属性
print b.name #打印b的属性
#Vergil
print id(a.__dict__)
#4337156456
print id(b.__dict__)
#4337156456
print a.__dict__ is b.__dict__
#True
参考文献
http://stackoverflow.com/questions/31875/is-there-a-simple-elegant-way-to-define-singletons/31887#31887
https://docs.python.org/2/reference/datamodel.html
http://code.activestate.com/recipes/66531/