python3 单例模式


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

使用单例模式的好处:

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

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

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

 

一、通过模块调用

在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))

 

二、使用__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))

 

三、使用装饰器

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函数,这是装饰器的特性。

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

 

四、使用元类

元类创建了所有的类型对象(包括object对象),系统默认的元类是type。

执行顺序:先定义metaclass,然后在类定义时,通过metaclass创建类,最后通过定义好的类创建实例。

所以,metaclass允许你创建类或者修改类。换句话说,可以把类看成是metaclass创建出来的“实例”

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

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

1、元类的使用

自定义元类时,通常继承自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")

2、元类实现单例模式

声明一个私有变量__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))

 

五、使用类

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()进行实例化得到的不是单例。


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM