Python 单例模式

  • Post author:
  • Post category:python


使用场景:

1)在我们的windows桌面上,我们打开了一个回收站,当我们试图再次打开一个新的回收站时,Windows系统并不会为你弹出一个新的回收站窗口。,也就是说在整个系统运行的过程中,系统只维护一个回收站的实例。这就是一个典型的单例模式运用。

继续说回收站,我们在实际使用中并不存在需要同时打开两个回收站窗口的必要性。假如我每次创建回收站时都需要消耗大量的资源,而每个回收站之间资源是共享的,那么在没有必要多次重复创建该实例的情况下,创建了多个实例,这样做就会给系统造成不必要的负担,造成资源浪费。

2)网站的计数器,一般也是采用单例模式实现,如果你存在多个计数器,每一个用户的访问都刷新计数器的值,这样的话你的实计数的值是难以同步的。但是如果采用单例模式实现就不会存在这样的问题,而且还可以避免线程安全问题。同样多线程的线程池的设计一般也是采用单例模式,这是由于线程池需要方便对池中的线程进行控制

3)对于一些应用程序的日志应用,或者web开发中读取配置文件都适合使用单例模式,如HttpApplication 就是单例的典型应用。

4)发送短信时,多个用户创建实例,可以使用单例

从上述的例子中我们可以总结出适合使用单例模式的场景和优缺点:


适用场景:


  • 1.需要生成唯一序列的环境

  • 2.需要频繁实例化然后销毁的对象。

  • 3.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。

  • 4.方便资源相互通信的环境

实现方式

– 使用模块

– 使用__new__

– 使用装饰器

– 使用元类(metaclass)


1)使用模块

Python模块的导入,一个模块创建类并实例化,另一个模块导入,此时多次调用就是单例,因为模块在第一次导入时,会生成.pyc文件,当第二次导入时,就会直接加载.pyc文件,而不会再次执行模块代码。因此我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。

# mysingle.py
class MySingle:
  def foo(self):
    pass

sinleton = MySingle()

将上面的代码保存在文件mysingle.py中,然后这样使用:
from mysingle import sinleton
singleton.foo()


2)使用__new__方法

实例化的过程其实不是直接调用init的,首先是new分配一块空间来创建实例,再由init对这个实例进行初始化.我们无法阻止new和init的调用,我们只能是限制他们的内容,以此使他们能达到单例的目的

__author__ = ''
__time__ = '2019/12/25 17:22'

class people(object):

    def __new__(cls,*args,**kargs):

        return super(people,cls).__new__(cls)

    def __init__(self,name):

        self.name = name

         

    def talk(self):

        print("hello,I am %s" %self.name)

     

     

     

class student(people):

    def __new__(cls,*args,**kargs):

        if not hasattr(cls,"instance"):

             

            cls.instance = super(student,cls).__new__(cls,*args,**kargs)

        return cls.instance



a = student("Timo")

print(a)

b = student("kysa")

c = student("Luyi")

a.talk()

b.talk()

print(c)

这里的输出结果是:

<__main__.student object at 0x0000025AC48BF2E8>

hello,I am Luyi

hello,I am Luyi

<__main__.student object at 0x0000025AC48BF2E8>

可以确定的是: 确实是单例了,因为a的id和b,c的id是一致的

但是为什么:a先创建明明是Timo,可是为什么a的name变成了Luyi呢?


原因:


虽然确实是a这个实例,但是在最后c重新调用了new,返回了a的实例,再经过init,改变了a的属性,执行时name ->Luyi.


解决:


这种情况下,我们只需要设置类变量,让init在类变量的限制下,只对类进行一次有效的初始化.


__author__ = ''
__time__ = '2019/12/25 17:22'

class people(object):

    def __new__(cls,*args,**kargs):

        return super(people,cls).__new__(cls)

    def __init__(self,name):

        self.name = name

         

    def talk(self):

        print("hello,I am %s" %self.name)

     

     

     

class student(people):

    def __new__(cls,*args,**kargs):

        if not hasattr(cls,"instance"):

            cls.instance = super(student,cls).__new__(cls,*args,**kargs)

        return cls.instance

    def __init__(self,name):

        if not hasattr(self,"init_fir"):

            self.init_fir = True

            super(student,self).__init__(name)

a = student("Timo")

print(a)

b = student("kysa")

c = student("Luyi")

a.talk()

b.talk()

print(c)


3)使用装饰器

装饰器里面的外层变量定义一个字典,里面存放这个类的实例.当第一次创建的收,就将这个实例保存到这个字典中.

然后以后每次创建对象的时候,都去这个字典中判断一下,如果已经被实例化,就直接取这个实例对象.如果不存在就保存到字典中

# encoding:utf-8
__author__ = ''
__time__ = '2019/12/25 17:22'


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(object):
    a = 1

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


a1 = A(2)
a2 = A(3)
print(id(a1), id(a2))


4)使用metaclass(元类)

元类可以控制类的创建过程,它主要做三件事:

– 拦截类的创建

– 修改类的定义

– 返回修改后的类

class Singleton2(type):
    def __init__(self, *args, **kwargs):
        self.__instance = None
        super(Singleton2,self).__init__(*args, **kwargs)

    def __call__(self, *args, **kwargs):
        if self.__instance is None:
            self.__instance = super(Singleton2,self).__call__(*args, **kwargs)
        return self.__instance


class Foo(object):
    __metaclass__ = Singleton2 #在代码执行到这里的时候,元类中的__new__方法和__init__方法其实已经被执行了,而不是在Foo实例化的时候执行。且仅会执行一次。


foo1 = Foo()
foo2 = Foo()
print (Foo.__dict__)  #_Singleton__instance': <__main__.Foo object at 0x100c52f10> 存在一个私有属性来保存属性,而不会污染Foo类(其实还是会污染,只是无法直接通过__instance属性访问)

print (foo1 is foo2)  # True

共享属性

创建实例时把所有实例的

__dict__

指向同一个字典,这样它们具有相同的属性和方法.

class Borg(object):
    _state = {}
    def __new__(cls, *args, **kw):
        ob = super(Borg, cls).__new__(cls, *args, **kw)
        ob.__dict__ = cls._state
        return ob

class MyClass2(Borg):
    a = 1



版权声明:本文为weixin_40475396原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。