• 设计模式——单例模式


    单例模式

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

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

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

  • 相关阅读:
    [转]How do I use variables in Oracle SQL Developer?
    [转]一张图理解prototype、proto和constructor的三角关系
    [转]ASP.NET Web API系列教程(目录)
    [转]解读ASP.NET 5 & MVC6系列(7):依赖注入
    [转]什么?你还不会写JQuery 插件
    [书目20170314]理解未来的7个原则
    java List.subList方法中的超级大陷阱
    MyBatis动态传入表名,字段名参数的解决办法---statementType用法
    lvs+keepalived和haproxy+heartbeat区别
    Nginx/LVS/HAProxy负载均衡软件的优缺点详解
  • 原文地址:https://www.cnblogs.com/ldy-miss/p/12975348.html
Copyright © 2020-2023  润新知