Python学习之——单例模式

发布于:2025-07-05 ⋅ 阅读:(17) ⋅ 点赞:(0)

参考

python之metaclass+singleton(一)
python之metaclass+singleton(二)
python之metaclass+singleton(三)

1 利用__metaclass__实现单例

揭开Python元类(metaclass)神秘的面纱

super的用法

【python】B站最细致的super()详解,一定有你不知道的知识!
在这里插入图片描述

super(arg1):
一个参数:
# 返回一个未bind的对象
ubo = super(Male)
# bind一个object
ubo.__get__(self).__init__(age, name)


super(arg1,arg2):
两个参数:
arg1:决定从MRO链中的arg1后开始
arg2:决定使用该super函数的对象object和对象的MRO,注意arg2也可以是class,但不常用

class Singleton(type)元类

singleton文件夹
在这里插入图片描述

__ init__.py文件

# -*- coding: utf-8 -*-
"""单例."""

class Singleton(type):
    def __init__(cls, name, bases, dict):
        super(Singleton, cls).__init__(name, bases, dict)
        cls.instance = None

    def __call__(cls, *args, **kwargs):
        print 'call Singleton __call__'
        if cls.instance is None:
            # 等价于cls.instance = type.__call__(cls, *args, **kwargs)
            cls.instance = super(Singleton, cls).__call__(*args, **kwargs)
        return cls.instance
 

利用__metaclass__实现单例的示例

class Foo(object):
    __metaclass__ = Singleton

foo1 = Foo()
foo2 = Foo()
#运行结果应该为True, 但实际是False, 想想为啥会出现这样的异常??
print foo1 is foo2  

请思考如下问题:

  1. 不用Foo类去创建实例,仅仅只有Foo和Singleton定义,执行脚本,会不会像第二节中打印出print语句?
    不会

  2. __call__在哪里调用?
    还记得__call__是怎么调用的吗?是一个类实例化出来的对象obj,直接通过obj ()形式调用了 __ call __。
    此处的元类根本没有复写 __new __和 __init __方法,Foo类就是Singleton创建出来的一个普通的类(就是一个Singleton类的对象,实例),因此Foo()会调用Singleton的 __call __。
    __call__中限定了只能新建一个Foo类的对象。如果想要定义的类是单例的,只要定义类时指定__metaclass__ = Singleton即可。

  3. 继承Foo类的也会是单例吗?

class FooChild(Foo):
    def __init__(self, a):
        super(FooChild, self).__init__()
foo11 = FooChild()
foo22 = FooChild()
print foo11 is foo22  #运行结果为True

2 重载__new__方法实现单例模式

“双重检查锁定”(Double-Checked Locking)单例模式

from threading import Lock

class SingletonClass(object):
    instance = None
    lock = Lock()
    
    def __new__(cls, *args, **kwargs):
        if cls.instance:
            return cls.instance
        with cls.lock:
            # double check
            if not cls.instance:
                cls.instance = super(SingletonClass, cls).__new__(cls, *args, **kwargs)
            return cls.instance

# 测试
if __name__ == "__main__":
    s1 = SingletonClass()
    s2 = SingletonClass()
    print(s1 is s2)  # 应该输出 True

    # 在多线程环境中测试
    import threading

    def test_singleton():
        instance = SingletonClass()
        print(f"Instance id in thread {threading.current_thread().name}: {id(instance)}")

    threads = [threading.Thread(target=test_singleton) for _ in range(5)]
    for t in threads:
        t.start()
    for t in threads:
        t.join()       

3 利用装饰器实现单例

def singleton(cls):
    instances = {}
    def wrapper(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return wrapper

@singleton
class Foo(object):
    pass

foo1 = Foo()

#等同于
#class Foo(object):
#    pass
#foo1 = singleton(Foo)
foo2 = Foo()
print foo1 is foo2  #运行结果为True

考虑一个类如果继承一个单例类的问题

3 中继承会出现问题

# 报错function() argument 'code' must be code, not str
# 因为Foo被装饰后成为了函数而不是class
# class FooChild(Foo):

# 改成如下
ClassFoo = Foo().__class__

class FooChild(ClassFoo):
    def __init__(self, a=0):
        super(FooChild, self).__init__()
但是这里的 FooChild就不在是单例了,也没法在加上@singleton来实现单例了