设计模式——单例模式
单例模式
单例模式是一种常用的设计模式,它确保一个类只有一个实例。而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。
如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。例如,一个系统只能有一个窗口管理器;一个系统中最好只有一个类实例读取配置文件,没有必要创建多个实例,否则浪费内存资源。
1. 基于__new__方法实现
判断系统是否已经有这个单例,如果有则返回,如果没有则创建。
类的__new__()
在__init__()
之前被调用,用于生成实例对象。利用这个特点可以实现单例模式。
class Singleton(object):
def __init__(self, age):
self.age = age
def __new__(cls, age, *args, **kwargs):
# 如果类没有_instance这个属性,那么就创建一个对象,并让_instance属性指向它。保证下次再
# 调用时就知道已经创建过对象了,这样就保证了只有1个对象
if not hasattr(cls, '_instance'):
cls._instance = object.__new__(cls, *args, **kwargs) # 只创建这一次
return cls._instance
# ======================调用======================
for x in range(5):
obj = Singleton(12)
print(id(obj)) # 是同一个实例对象
# 140221886024616
# 140221886024616
# 140221886024616
# 140221886024616
# 140221886024616
一个小问题:上述代码单线程运行时,无论创建调用多少次Singleton(),只创建一个示例。多进程多线程运行时,为了保证线程安全需要在内部加入锁。添加三行代码即可:
import threading
class Singleton(object):
_instance_lock = threading.Lock() # 加锁
def __init__(self, age):
self.age = age
def __new__(cls, age, *args, **kwargs):
# 如果类没有_instance这个属性,那么就创建一个对象,并让_instance属性指向它。保证下次再
# 调用时就知道已经创建过对象了,这样就保证了只有1个对象
with Singleton._instance_lock: # 锁
if not hasattr(cls, '_instance'):
cls._instance = object.__new__(cls, *args, **kwargs) # 只创建这一次
return cls._instance
# ======================调用======================
def func():
x = Singleton(12)
print(id(x))
thread_wait = []
for i in range(1000):
t = threading.Thread(target=func)
t.start()
thread_wait.append(t)
for x in thread_wait:
x.join()
# 140451708398952
# ...
# 全部是相同ID,是同一个对象
2. Python模块import方法(线程安全)
其实,Python 的模块就是天然的单例模式,因为模块在第一次导入时,会生成 .pyc
文件,当第二次导入时,就会直接加载 .pyc
文件,而不会再次执行模块代码。因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。(Excellent !!)
mysingleton.py
class Singleton(object):
def func(self):
pass
singleton = Singleton()
将上面的代码保存在文件 mysingleton.py
中,要使用时,直接在其他文件中导入此文件中的对象,这个对象即是单例模式的对象
# 调用
from mysingleton import singleton
singleton.func()
socweb中的db就是单例模式,整个项目中只有一个数据库示例。
# socweb/app/engines/__init__.py
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session
from sqlalchemy.orm import sessionmaker
from app.config import Config
class DBEngine:
def __init__(self):
# 用来初始化数据库连接
self.engine = create_engine(Config.SQLALCHEMY_DATABASE_URI, convert_unicode=True,
encoding='utf-8', pool_size=64, max_overflow=0,
pool_recycle=30, pool_timeout=10)
# 创建DBSession类型, scoped_session的目的主要是为了线程安全, 使session实现了线程隔离,
# 在同一个线程中,call scoped_session 的时候,返回的是同一个对象, 这样我们就只能看见本线程的session。
self.session = self.create_scoped_session()
def create_scoped_session(self):
session_maker = sessionmaker(bind=self.engine)
return scoped_session(session_maker)
db = DBEngine() # 单例模式,龙哥 niu 13
调用
# socweb/app/businesses/base_biz.py
from app.engines import db
class BaseBiz:
def __init__(self):
self.ses = db.session
self.allow_query_all = False
self.model = None
# ...
优缺点
优点:
- 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例。
缺点:
-
(1) 单例模式没有抽象层,扩展很困难,若要扩展,除了修改代码基本上没有第二种途径可以实现。
-
(2) 单例类的职责过重,在一定程度上违背了“单一职责原则”。