Python--单例模式

一、什么是单例模式

一个类,不断地实例化得到的对象,其实都是不同的对象

如果一个类实例化的对象永远是同一个对象的话,那么就可以称之为单例模式

比如,某个服务器的配置信息存在在一个文件中,客户端通过AppConfig类来读取配置文件的信息.

如果程序的运行的过程中,很多地方都会用到配置文件信息,则就需要创建很多的AppConfig实例,这样就导致内存中有很多AppConfig对象的实例,造成资源的浪费.其实这个时候AppConfig我们希望它只有一份,就可以使用单例模式.

二、实现单例模式的方法

2.1、使用模块

一个模块在第一次导入的时候,会执行模块代码,生成.pyc文件,当第二次导入的时候,就会直接加载.pyc文件。

我们把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了

新建一个python模块叫singleton,然后放入一个 py 文件,取名为 mysingleton.py
mysingleton.py

class Singleton(object):
    def foo(self):
        pass
singleton = Singleton()

使用

from singleton.mysingleton import singleton

这样,每次拿到的都是同一个对象

2.2、使用装饰器


def singleton(cls):
    # 单下划线的作用是这个变量只能在当前模块里访问,仅仅是一种提示作用
    # 创建一个字典用来保存类的实例对象
    _instance = {}

    def _singleton(*args, **kwargs):
        # 先判断这个类有没有对象
        if cls not in _instance:
            _instance[cls] = cls(*args, **kwargs)  # 创建一个对象,并保存到字典当中
        # 将实例对象返回
        return _instance[cls]

    return _singleton


@singleton
class A():
    a = 1

    def __init__(self, x=0):
        self.x = x
        print('这是A的类的初始化方法')


a1 = A(2)
a2 = A(3)

print(a1.__dict__ ,a2.__dict__)
print(id(a1), id(a2))

在这里插入图片描述

2.3、调用类-基于 classmethod 装饰器

思路就是, 调用类自定义的 _instance属性 , 这样有一个弊端就是在使用类创建的时候,并不是单例了.也就是说在创建类的时候一定要用类里面规定的方法创建

class Singleton(object):
    def __init__(self,*args,**kwargs):
        pass

    @classmethod
    def get_instance(cls, *args, **kwargs):
        # 利用反射,看看这个类有没有_instance属性
        if not hasattr(Singleton, '_instance'):
            Singleton._instance = Singleton(*args, **kwargs)

        return Singleton._instance


s1 = Singleton()  # 使用这种方式创建实例的时候,并不能保证单例
s3 = Singleton()
print(id(s1),id(s3))

s2 = Singleton.get_instance()  # 只有使用这种方式创建的时候才可以实现单例
s4 = Singleton.get_instance()
print(id(s2),id(s4))

在这里插入图片描述

这种方法过于复杂并不推荐使用

import time
import threading

class Singleton(object):
    _instance_lock = threading.Lock()

    def __init__(self,*args,**kwargs):
        time.sleep(1)

    @classmethod
    def get_instance(cls,*args,**kwargs):
        if not hasattr(Singleton,'_instance'):
            with Singleton._instance_lock:
                if not hasattr(Singleton,'_instance'):
                    Singleton._instance = Singleton(*args,**kwargs)

        return Singleton._instance

def task(arg):
    obj = Singleton.get_instance(arg)
    print(obj)

for i in range(10):
    t = threading.Thread(target=task,args=[i,])
    t.start()

obj = Singleton.get_instance()
print(obj)

2.4、重写__new__方法

1、一个对象的实例化过程是先执行类的__new__方法,如果我们没有写,默认会调用object的__new__方法,返回一个实例化对象,然后再调用__init__方法,对这个对象进行初始化,我们可以根据这个实现单例.
2、在一个类的__new__方法中先判断是不是存在实例,如果存在实例,就直接返回,如果不存在实例就创建

class Mysingle():
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls,'new_obj'):
            cls.new_obj = super().__new__(cls)
        return cls.new_obj

    def __init__(self, name):
        self.name = name


obj1 = Mysingle('frank')
print(id(obj1), obj1.__dict__)

obj2 = Mysingle('jason')
print(id(obj2), obj2.__dict__)

2.5 重写元类的 call 方法

class Single(type):

    def __call__(self, *args, **kwargs):
        if not hasattr(self,'new_obj'):
            self.new_obj = super().__call__(*args, **kwargs)

        return self.new_obj


class Mysingle(metaclass=Single):
    def __init__(self, name):
        self.name = name


obj1 = Mysingle('frank')
print(id(obj1), obj1.__dict__)

obj2 = Mysingle('jason')
print(id(obj2), obj2.__dict__)

**步骤分析: **

1:obj1 = Mysingle(‘frank’) --> 调用 Single 元类的_ _ call_ _ 方法

2:self.new_obj = None—>调用 type 元类的_ _ call_ _ 方法最终将初始化好的对象返回

3:将初始化好的对象 赋值给 new_obj 属性,然后Single 元类的_ _ call_ _ 方法 将这个对象返回 并赋值给 obj1

4:得到 obj1—>obj1._ _ dict _ _= {‘name’: ‘frank’}

5:obj2 = Mysingle(‘jason’) --> 调用 Single 元类的_ _ call_ _ 方法

6:self.new_obj 此时并不是 None —> 那么直接返回之前已经生成的对象,此时不经过 Mysingle类的_ _ init_ _ 方法

7:obj2 就是之前已经生成对象–> obj2._ _ dict _ _ = {‘name’: ‘frank’}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值