大厂面试真实案例!Python实现单例模式的五种写法!转载

原创
小哥 3年前 (2022-10-16) 阅读数 56 #大杂烩

大家好,我是菜鸟兄弟!大家好,我是菜鸟兄弟!大家好,我是菜鸟兄弟!大家好,我是菜鸟兄弟!

大厂面试python如果你不提前做好准备,突然问,老鸟有点不知所措。单人模式是什么?

单件模式(单件模式Singleton Pattern) 是一种常见的软件设计模式,其主要目的是确保特定类只存在一个实例。当您只想让一个类的一个实例出现在整个系统中时,Singleton对象就派上了用场。

例如,服务器程序的配置信息存储在文件中,客户端可以通过 AppConfig 类从配置文件中读取信息。如果在程序运行期间有许多地方使用配置文件的内容,即需要在许多地方创建 AppConfig 对象,这导致存在多个 AppConfig 实例对象,这可能会严重浪费内存资源,特别是在存在大量配置文件内容的情况下。

事实上,类似于事实,类似于 AppConfig 对于这样的类,我们希望在程序运行时期间只存在一个实例对象。

在 Python 单例模式可以通过多种方式实现。

  1. 使用模块

  2. 使用装饰器使用装饰器使用装饰器使用装饰器

  3. 使用类

  4. 基于 __new__ 方法实现

  5. 基于 metaclass 方式实现

以下是详细情况。在此详细说明。以下是一些细节。以下是更多细节。

使用模块

其实,Python 模块是一种自然的单例模式,因为模块生成 .pyc 文件,当第二次导入完成时,它将直接加载 .pyc 文件,而无需再次执行模块代码。

因此,我们只需要在一个模块中定义相关的函数和数据,就可以获得单个实例对象。

如果我们真的想要一个单人班,考虑一下。

class Singleton(object):
    def foo(self):
        pass
singleton = Singleton()

将上述代码保存在文件中 mysingleton.py 如果您想使用它,可以将该文件中的对象直接导入到其他文件中,该对象是Singleton模式的对象

from mysingleton import singleton

使用装饰器使用装饰器使用装饰器使用装饰器

def Singleton(cls):
    _instance = {}

    def _singleton(*args, **kargs):
        if cls not in _instance:
            _instance[cls] = cls(*args, **kargs)
        return _instance[cls]

    return _singleton

@Singleton
class A(object):
    a = 1

    def __init__(self, x=0):
        self.x = x

a1 = A(2)
a2 = A(3)

使用类

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

一般来说,人们认为这就完成了单例模式,但在使用多线程时会出现问题。

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

def task(arg):
    obj = Singleton.instance()
    print(obj)

for i in range(10):
    t = threading.Thread(target=task,args=[i,])
    t.start()

执行程序后,将打印以下结果。

<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>

似乎也不是问题,那是因为执行太快了,如果 __init__ 有一些方法有很多方法有几种方法 IO 操作时,您会发现问题所在。操作时,您会看到问题所在。手术,你就会发现问题所在。操作,就会发现问题所在。

这里我们穿过下面我们穿过这里我们穿过下面我们穿过 time.sleep 模拟,我们有上面的模拟 __init__ 下面的代码被添加到该方法中。方法,并使用以下代码。

def __init__(self):
    import time
    time.sleep(1)

重新执行程序后,结果如下所示。

<__main__.Singleton object at 0x034A3410>
<__main__.Singleton object at 0x034BB990>
<__main__.Singleton object at 0x034BB910>
<__main__.Singleton object at 0x034ADED0>
<__main__.Singleton object at 0x034E6BD0>
<__main__.Singleton object at 0x034E6C10>
<__main__.Singleton object at 0x034E6B90>
<__main__.Singleton object at 0x034BBA30>
<__main__.Singleton object at 0x034F6B90>
<__main__.Singleton object at 0x034E6A90>

出现了一个问题!如上创建的单实例不支持多线程。

解决方案:锁定!解锁部分并行执行,锁定部分串联执行,降低了速度,但保证了数据安全。

import time
import threading

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

    def __init__(self):
        time.sleep(1)

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

def task(arg):
    obj = Singleton.instance()
    print(obj)
    

for i in range(10):
    t = threading.Thread(target=task,args=[i,])
    t.start()

time.sleep(20)
obj = Singleton.instance()
print(obj)

打印结果如下。打印输出结果如下。结果打印如下。打印输出如下所示。

<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>

差不多就是这样,但是当程序执行时仍然有一个小问题, time.sleep(20) 之后,当实例化下面的对象时,它此时已经是单例模式。

但我们还是加了锁,这不是很好,然后进行了一些优化 intance 方法,只需将其更改为以下内容即可。

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

这样,可以支持多个线程的单实例模式就完成了。 +

import time
import threading

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

    def __init__(self):
        time.sleep(1)

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

def task(arg):
    obj = Singleton.instance()
    print(obj)
    
    
for i in range(10):
    t = threading.Thread(target=task,args=[i,])
    t.start()
    
    
time.sleep(20)
obj = Singleton.instance()
print(obj)

以这种方式实现的单例模式在使用上受到限制,以后的实例化必须通过 obj = Singleton.instance()

如果用 obj = Singleton() 这不是一个单一的例子。这不是一个单一的案例。这不是一件独一无二的事情。

基于 __new__ 方法实现

通过上面的例子,我们可以知道当我们实现单个实例时,为了确保线程安全,我们需要在内部添加锁。

我们知道,当我们实例化一个对象时,我们首先执行类 __new__ 方法(当我们不编写它时,默认调用是 object.__new__ ),实例化对象;然后执行类 __init__ 方法来初始化这个对象,我们所能做的就是在此基础上实现单例模式。

import threading

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

    def __init__(self):
        pass

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

obj1 = Singleton()
obj2 = Singleton()
print(obj1,obj2)

def task(arg):
    obj = Singleton()
    print(obj)

for i in range(10):
    t = threading.Thread(target=task,args=[i,])
    t.start()

打印结果如下。打印输出结果如下。结果打印如下。打印输出如下所示。

<__main__.Singleton object at 0x038B33D0> <__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>
<__main__.Singleton object at 0x038B33D0>

使用单例模式的这种方法,稍后将以与通常实例化相同的方式实例化对象 obj = Singleton()

基于 metaclass 方式实现

相关知识:

  1. 类由 type 创建,在创建类时,创建,在创建类时创建,在创建类时type 的 __init__ 方法被自动执行,类方法被自动执行,类() 执行 type 的 __call__ 方法(类的 __new__ 方法、类方法、类方法的类方法、 __init__ 方法)

  2. 对象由类创建,在创建对象时,类 __init__ 方法被自动执行,对象方法被自动执行,对象()执行类的 __call__ 方法

例子:

class Foo:
    def __init__(self):
        pass

    def __call__(self, *args, **kwargs):
        pass

obj = Foo()
# 执行type的 __call__ 方法,则调用该方法,该方法调用调用该方法的方法,并调用 Foo类(是type的宾语(宾语)(宾语) __new__方法,该方法用于创建对象,然后调用 Foo类(是type的宾语(宾语)(宾语) __init__方法,该方法用于初始化对象。方法,用于初始化对象。用于初始化对象的方法。方法来初始化该对象。

obj()    # 执行Foo的 __call__ 方法

元类的使用。元类的使用。元类的使用。元类的使用。

class SingletonType(type):
    def __init__(self,*args,**kwargs):
        super(SingletonType,self).__init__(*args,**kwargs)

    def __call__(cls, *args, **kwargs): # 这里的cls,即Foo类
        print(cls,cls)
        obj = cls.__new__(cls,*args, **kwargs)
        cls.__init__(obj,*args, **kwargs) # Foo.__init__(obj)
        return obj

class Foo(metaclass=SingletonType): # 指定创建Foo的type为SingletonType
    def __init__(self,name):
        self.name = name
    def __new__(cls, *args, **kwargs):
        return object.__new__(cls)

obj = Foo(xx)

实现单例模式。单例模式的实现。实施单件模型。实现单例模式。

import threading

class SingletonType(type):
    _instance_lock = threading.Lock()
    def __call__(cls, *args, **kwargs):
        if not hasattr(cls, "_instance"):
            with SingletonType._instance_lock:
                if not hasattr(cls, "_instance"):
                    cls._instance = super(SingletonType,cls).__call__(*args, **kwargs)
        return cls._instance

class Foo(metaclass=SingletonType):
    def __init__(self,name):
        self.name = name

obj1 = Foo(name)
obj2 = Foo(name)
print(obj1,obj2)

作者:听风作者:听风

https://www.cnblogs.com/huchong/p/8244279.html

推荐阅读:
入门: 最完整的零基学习最全面的零基学习最完整的零基学习Python的问题  | 从零开始学习从零基础学习从零基础学习8个月的Python  | 实战项目 |学Python这是捷径这是捷径就是这条捷径
干货:爬行豆瓣短评,电影《后来的我们》 | 38年NBA最佳球员分析最佳球员分析 |   从万众期待到口碑惨败!唐探3令人失望  | 笑新一天图龙记笑新一天图龙集 | 谜语之王回答灯谜之王灯谜之王谜语之王 |用Python人山人海素描图人山人海素描图人山人海 Dishonor太火了,我用机器学习做了一个迷你推荐系统电影
趣味:弹球游戏  | 九宫格  | 漂亮的花 | 两百行Python日常酷跑游戏日常酷跑游戏日常酷跑游戏!
AI: 会写诗的机器人会写诗的机器人会写诗的机器人 | 给图片上色给图片上色给图片上色 | 预测收入 | 《耻辱》太火了,我用机器学习做了一部迷你推荐系统电影
小工具: Pdf转Word易于修复表单和水印!易于处理的表单和水印!易于修复表单和水印!简单的表格和水印! | 一键把html将页面另存为网页另存为网页另存为pdf!|  再见PDF提款费!提款费!提款费!提款费用! | 用90构建最强大的代码行构建最强大的代码行构建最强大的代码行PDF转换器,word、PPT、excel、markdown、html一键转换 | 制作一个固定的低成本机票提醒!制作一张别针的低价机票提醒! |60代码行做了一个语音墙纸切换,天天见女士!

年度弹出式文案年度弹出式文案

  • 1). 卧槽!Pdf转Word用Python轻松搞定 !

  • 2).学Python闻起来好香!我用100一行代码做了一个网站,帮助人们做了一行代码,做了一个网站,帮助了人们做了一行代码,帮助了人们PS旅行图片赚鸡腿吃旅行图片赚鸡腿

  • 3).第一次播放量过亿,火爆全网,我分析了《波妹》,发现了这些秘密

  • 4). 80一行行代码!使用Python让救济金做正确的事做做的人做好事的人A梦分身

  • 5).你必须掌握的东西你必须掌握20个python代码,简短而紧凑,永无止境的有用代码,简短而甜蜜,永无止境的有用的代码,简短而紧凑,永无止境的使用代码,简短而甜蜜,永无止境的用途

  • 6). 30个Python古怪技能集古怪小贴士收藏古怪技能集

  • 7). 我总结的80《菜鸟学习专页》《菜鸟学习专页》《菜鸟学习》Python精选干货.pdf》,都是干货

  • 8). 再见Python!我要学Go了!2500词深度分析词深度分析词深度分析 !

  • 9).发现一只舔狗的福利!这Python爬虫神器太酷了,不能自动下载女孩的照片

点击阅读原文点击查看点击点击阅读点击阅读原文点击查看B放我鸽子看录像!站在我的录像带上!在视频里放我鸽子!站在我的录像带上!

版权声明

所有资源都来源于爬虫采集,如有侵权请联系我们,我们将立即删除

热门