设计模式——单例模式

单例模式

单例模式是一种常用的设计模式,它确保一个类只有一个实例。而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。

如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。例如,一个系统只能有一个窗口管理器;一个系统中最好只有一个类实例读取配置文件,没有必要创建多个实例,否则浪费内存资源。

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) 单例类的职责过重,在一定程度上违背了“单一职责原则”。

posted @ 2020-05-27 18:37  961897  阅读(200)  评论(0编辑  收藏  举报