使用场景
单例确保一个类只有一个实例存在。比如在服务器的配置信息存放一个文件,客户端通过一个 AppConfig 的类来读取配置文件的信息,如果在程序运行期间,很多地方都需要使用配置文件的内容,也就是说很多地方都需要创建 AppConfig 对象的实例,这就导致系统中存在多个 AppConfig 的实例对象,会浪费内存。这时就应该使用单例,确保只有一个实例。
Python 实现单例
- 使用模块
- 使用 new
- 使用装饰器
- 使用元类
使用模块
原理:Python 的模块就是天然的单例。模块第一次导入时会生成 .pyc,第二次导入就会直接加载 .pyc,不会执行模块代码。
# singleton.py
class Singleton:
def foo(self):
pass
# 注意这行
my_singleton = Singleton()
调用
from singleton import my_singleton
my_singleton.foo()
使用 __new__
# __new__ 是拦截器,会在 __init__ 之前被调用
class Singleton:
_instance = None
def __new__(cls, *args, **kw):
if not cls._instance:
# cls._instance = object.__new__(cls, *args, **kw)
cls._instance = super().__new__(cls, *args, **kw)
return cls._instance
if __name__ == '__main__':
s = Singleton()
print(s)
使用装饰器
from functools import wraps
def single_dec(cls):
_instance = {}
@wraps(cls)
def get_instance(*args, **kwargs):
if cls not in _instance:
_instance[cls] = cls()
return _instance[cls]
return get_instance
@single_dec
class Single:
pass
if __name__ == '__main__':
s = Single()
使用元类
注意使用元类和使用 __new__
的不同。
class Singleton(type):
_instance = None
def __call__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super().__call__(*args, **kwargs)
return cls._instance
class SingleIns(metaclass=Singleton):
pass
if __name__ == '__main__':
s = SingleIns()
ss = SingleIns()
print(id(s), id(ss))
Java 中使用单例
class Soup2{
private Soup2(){}
private static Soup2 ps1 = new Soup2();
public static Soup2 access(){
return ps1;
}
public void f() {}
}
把构造器声明为 private 可以阻止直接创建某个类的实例;
如果没有创建构造器的话,编译器会默认创建一个不带任何参数的构造器。
所以,为了阻止编译器创建默认的构造器,需要手动创建一个构造器。