03 November 2018

Python 单例实现方法

python 实现单例方法有多种,下面我们介绍几种常用的方法。

1. 使用元类实现单例

  • 创建一个 SingleInstance 的元类
  • 类指定 metaclass = SingleInstance
# 创建一个 metaclass 类
class SingleInstance(type):
    """
    类创建对象的时候会调用该方法, 返回类对象实例
    """
    def __call__(cls, *args, **kwargs):
        instance = getattr(cls, '__instance__', None)
        
        if instance is None:
            instance = super(SingleInstance, cls).__call__(*args, **kwargs)
            setattr(cls, '__instance__', instance)
            print("create instance ", instance)
        else:
            print("use old instance ", instance)
        print('got instance for ', cls)
        return instance


"""
测试方法:
1. 每次实验创建两个类,确认不同的类之间不会相互影响
2. 每个类创建两个对象,确认两个对象是一样的
"""
class TestClass0(metaclass=SingleInstance):
    def __init__(self):
        print("init test0 class object")


class TestClass1(metaclass=SingleInstance):
    def __init__(self):
        print("init test1 class object")
        

# 测试第一个类对象
test00 = TestClass0()
test01 = TestClass0()
print(test00, test01)

print('\n----------------------\n')

# 测试第二个类对象
test10 = TestClass1()
test11 = TestClass1()
print(test10, test11)
init test0 class object
create instance  <__main__.TestClass0 object at 0x7f5492fe66a0>
got instance for  <class '__main__.TestClass0'>
use old instance  <__main__.TestClass0 object at 0x7f5492fe66a0>
got instance for  <class '__main__.TestClass0'>
<__main__.TestClass0 object at 0x7f5492fe66a0> <__main__.TestClass0 object at 0x7f5492fe66a0>

----------------------

init test1 class object
create instance  <__main__.TestClass1 object at 0x7f5492fe69b0>
got instance for  <class '__main__.TestClass1'>
use old instance  <__main__.TestClass1 object at 0x7f5492fe69b0>
got instance for  <class '__main__.TestClass1'>
<__main__.TestClass1 object at 0x7f5492fe69b0> <__main__.TestClass1 object at 0x7f5492fe69b0>

2. 通过类的 new 方法实现

  • 第一次创建对象并保存到类属性中
  • 后续从类属性中取出对象,不再创建新的对象
class SingleClass():
    def __new__(cls, *args, **kwargs):
        print('new instance for ', cls)
        
        if not hasattr(cls, '__instance__'):
            print('create instance for ', cls)
            instance = super(SingleClass, cls).__new__(cls, *args, **kwargs)
            cls.__instance__ = instance
        
        return cls.__instance__


class SubClass(SingleClass):
    pass

test00 = SingleClass()
test01 = SingleClass()
print(test00, test01)

print('\n----------------------\n')

test10 = SubClass()
test11 = SubClass()
print(test10, test11)


new instance for  <class '__main__.SingleClass'>
create instance for  <class '__main__.SingleClass'>
new instance for  <class '__main__.SingleClass'>
<__main__.SingleClass object at 0x7f5492fc75f8> <__main__.SingleClass object at 0x7f5492fc75f8>

----------------------

new instance for  <class '__main__.SubClass'>
new instance for  <class '__main__.SubClass'>
<__main__.SingleClass object at 0x7f5492fc75f8> <__main__.SingleClass object at 0x7f5492fc75f8>

我们发现这样实现是有问题的,SubClass 无法创建自己的单例。 因为父类 SingleClass 的类对象数据会被所有子类共享,导致所有 SingleClass 子类无法创建新的对象。

解决方法是我们在 SingleClass 类中使用字典保存每个子类的实例对象,改进后代码如下:

class SingleTClass():
    __instance__ = {}
    
    def __new__(cls, *args, **kwargs):
        print('new instance for ', cls)
        
        if not cls in cls.__instance__:
            print('create instance for ', cls)
            instance = super(SingleTClass, cls).__new__(cls, *args, **kwargs)
            cls.__instance__[cls] = instance

        return cls.__instance__[cls]


class SubTClass(SingleTClass):
    pass

test00 = SingleTClass()
test01 = SingleTClass()
print(test00, test01)

print('\n----------------------\n')

test10 = SubTClass()
test11 = SubTClass()
print(test10, test11)
new instance for  <class '__main__.SingleTClass'>
create instance for  <class '__main__.SingleTClass'>
new instance for  <class '__main__.SingleTClass'>
<__main__.SingleTClass object at 0x7f5492fc7ac8> <__main__.SingleTClass object at 0x7f5492fc7ac8>

----------------------

new instance for  <class '__main__.SubTClass'>
create instance for  <class '__main__.SubTClass'>
new instance for  <class '__main__.SubTClass'>
<__main__.SubTClass object at 0x7f5492fc7438> <__main__.SubTClass object at 0x7f5492fc7438>

3. 通过装饰器实现单例

  • 创建一个 singleclass 的装饰器
  • 创建类的时候使用上述装饰器
from functools import wraps

"""
把类对象变成一个函数,每次调用类对象创建实例的时候,实际是调用装饰器返回的函数
"""
def singleclass(cls):
    __instance__ = {}
    
    @wraps(cls)
    def cls_instance(*args, **kwargs):
        print('new instance for ', cls)
        if not cls in __instance__:
            __instance__[cls] = cls(*args, **kwargs)
        return __instance__[cls]

    return cls_instance

@singleclass
class TestD1Class():
    pass

@singleclass
class TestD2Class():
    pass



test00 = TestD1Class()
test01 = TestD1Class()
print(test00, test01)

print('\n----------------------\n')

test10 = TestD2Class()
test11 = TestD2Class()
print(test10, test11)
new instance for  <class '__main__.TestD1Class'>
new instance for  <class '__main__.TestD1Class'>
<__main__.TestD1Class object at 0x7f5492fee518> <__main__.TestD1Class object at 0x7f5492fee518>

----------------------

new instance for  <class '__main__.TestD2Class'>
new instance for  <class '__main__.TestD2Class'>
<__main__.TestD2Class object at 0x7f5492fee160> <__main__.TestD2Class object at 0x7f5492fee160>