python3 单例模式

单例模式是某个类在整个系统中只存在一个实例的一种设计模式。

使用单例模式的好处:python

  1. 单例模式不只能够减小内存资源占用,并且由于只初始化一次,还能够加快运行性能。例如当程序经过一个类来读取配置信息,而程序多个地方须要使用配置信息,这时整个程序运行过程当中只需一个实例对象便可,可减小占用内存资源,同时还能够保证程序在多处地方获取的配置信息一致。
  2. 使用单例模式可进行同步控制,计数器同步、程序多处读取配置信息这些情景下若只存在一个实例,便可保证一致性。

Python实现单例模式有4种方式:

  1. 经过模块调用
  2. 使用__new__方法
  3. 使用装饰器
  4. 使用元类(metaclass) 

 

1、经过模块调用

在python3中,首次导入模块文件时,会在程序目录下的__pycache__目录中生成pyc文件,再导入时,将直接加载pyc文件。所以,只需把相关的函数和数据定义在一个模块中,就能够得到一个单例对象了。设计模式

  • 定义singleton_demo.py模块
class singleton_cal:
    def foo(self)
        pass
 

export_singleton = singleton_cal()
  • 使用模块
from singleton_demo import export_singleton


a = export_singleton
b = export_singleton

print(id(a) == id(b))

 

2、使用__new__方法

  1. __new__:建立实例对象时调用的构造方法
  2. __init__ :实例初始化方法,用于设置实例的相关属性

当实例化一个对象时,先调用__new__方法(未定义时调用object.__new__)实例化对象,而后调用__init__方法进行对象初始化。安全

因此,能够声明一个私有类变量__instance。当__instance不为None时,表示系统中已有实例,直接返回该实例;若__instance为None时,表示系统中尚未该类的实例,则建立新实例并返回。多线程

class Singleton(object):
    __instance = None
def __new__(cls, *args, **kwargs): if not cls.__instance: cls.__instance = super().__new__(cls, *args, **kwargs) return cls.__instance a = Singleton() b = Singleton() print(id(a) == id(b))

 

3、使用装饰器

from functools import wraps


def singleton(cls):
    instances = {}
@wraps(cls)
def get_instance(*args, **kwargs): if cls not in instances: instances[cls] = cls(*args, **kwargs) return instances[cls] return get_instance @singleton class Singleton(object): def foo(self): pass a = Singleton() b = Singleton() print(id(a) == id(b))

只有第一次调用Singleton类时,装饰器才会从instances={}开始执行,之后再调用该类时,都只执行get_instance函数,这是装饰器的特性。并发

利用装饰器的这个特性,能够实现单例模式。复用装饰器,可使多个类实现单例模式。函数

 

4、使用元类

元类建立了全部的类型对象(包括object对象),系统默认的元类是type。性能

执行顺序:先定义metaclass,而后在类定义时,经过metaclass建立类,最后经过定义好的类建立实例。spa

因此,metaclass容许你建立类或者修改类。换句话说,能够把类当作是metaclass建立出来的“实例”线程

元类中定义的__new__方法,在以该类为元类的类定义时自动调用。例如:类A以类B为元类,当定义类A时,类B的__new__方法将会被自动调用。设计

元类中定义的__call__方法,在以该类为元类的类建立实例时自动调用。例如:类A以类B为元类,当类A建立实例时,类B的__call__方法将会被自动调用。

一、元类的使用

自定义元类时,一般继承自type。

class MetaClass(type):
    def __init__(cls, *args, **kwargs):
# cls 代指以该类为元类的类 Foo super(MetaClass, cls).
__init__(*args, **kwargs)

def __new__(mcs, *args, **kwargs):
# mcs 代指元类自身
print("MetaClass.__new__: ", mcs)
return super().__new__(mcs, *args, **kwargs)
def __call__(cls, *args, **kwargs):
# cls 代指以该类为元类的类 Foo
print("CLS: ", cls) obj = cls.__new__(cls, *args, **kwargs) cls.__init__(obj, *args, **kwargs) return obj class Foo(metaclass=MetaClass):
# 定义类Foo时,将调用元类的__new__方法和__init__方法。就跟通常普通类实例化时调用__new__方法和__init__方法同样。
def __init__(self, name): self.name = name

# Foo 实例化时会调用元类的__call__方法。 a
= Foo("ABC")

二、元类实现单例模式

声明一个私有变量__instance保存类实例。__instance为None时,调用type的__call__方法为类建立实例。

class SingletonMeta(type):
    __instance = None
    def __call__(cls, *args, **kwargs):
        if not cls.__instance:
            cls.__instance = type.__call__(cls, *args, **kwargs)
        return cls.__instance


class MyClass(metaclass=SingletonMeta):
    def foo(self):
        pass


a = MyClass()
b = MyClass()
print(id(a) == id(b))

 

5、使用类

class Singleton(object):
    def __init__(self):
        pass

    @classmethod
    def instance(cls, *args, **kwargs):
        if not hasattr(Singleton, "_instance"):
            Singleton._instance = Singleton(*args, **kwargs)
        return Singleton._instance

线程安全

经过以上方法定义的单例模式,没法支持多线程。解决这个问题的办法是:加锁!未加锁部分并发执行,加锁部分串行执行。

import threading


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

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

def task(arg): obj = Singleton.instance() print("Task {}".format(arg), id(obj)) for i in range(10): t = threading.Thread(target=task, args=[i,]) t.start()

 使用类实现的单例模式,在使用时必须经过Singleton.instance()进行实例化。若是使用Singleton()进行实例化获得的不是单例。

相关文章
相关标签/搜索