Python3实现单例模式

文末有一些补充内容,如果你不太理解本文中提到的这些函数的意义或者不知道元类是什么东西,可以先查看补充内容,以帮助你做好理解本文所必要的知识储备

1. 在进入正题之前,我们需要先了解两个有特殊用途的函数,
__new__()__call__().

__new__()
它是在创建实例的时候被调用(注意此处的"实例",我在这里并没有说"的实例",因为除了类,还有元类,元类创建实例的时候也会调用这个函数)

__call__()
官方定义:Called when the instance is "called" as a function; if this method is defined, x(arg1, arg2, ...) is a shorthand for x.__call__(arg1, arg2, ...).
它是在“实例被当成函数调用时”被调用。
举个例子,实例如果是"Demo",那么,当你写下"Demo()"的时候,该实例(即Demo)的创建者(注意:此处提到的创建者既有可能是类,也有可能是元类)中的__call__()被调用。

如果这个实例是一个类,那么它的创建者就是一个元类,如果这个实例是一个对象,那么它的创建者就是一个类。

所以,要利用Python实现单例模式,我们也有两种思路:

2. (下面是正题,我们来谈一谈如何在Python3中实现单例模式):

第一种思路是利用元类,元类的实例是类,而类被当成函数调用时不就是对象吗?(假设类名是Demo,则Demo()就创建并返回了一个对象)因此,我们可以通过定制元类中的__call__()来实现单例。
代码如下:

class Singleton(type):
    def __call__(cls, *args, **kwargs):
        if not hasattr(cls, '_instance'):
            cls._instance = super().__call__(*args, **kwargs)
        return cls._instance

class Foo(metaclass=Singleton):
    pass

这里,Foo是元类"Singleton"的实例,Foo()就是元类"Singleton"的实例被当成函数在调用,因此元类"Singleton"中的__call__()就会被调用。

我们输入以下代码来测试一下:

a = Foo()
b = Foo()
print(id(a)==id(b))  # 得到 True

第二种思路则不需要用到元类。
由于__new__()是在创建实例的时候被调用(即,如果你创建一个类Foo,并且这个Foo中含有一个__new__()的方法,那么该方法将在你创建这个Foo类的对象时被调用,即在你写Foo()的时候被调用),所以我们可以通过直接定制__new__()来实现单例。
代码如下:

class Foo(object):
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, '_instance'):
            cls._instance = super().__new__(cls, *args, **kwargs)
        return cls._instance

测试一下:

a = Foo()
b = Foo()
print(id(a)==id(b))  # 得到 True

当然,如果你不想在当前这个类中定制__new__()方法,你也可以在当前类的父类中定制__new__(),python的解释器依然能够找到并正确执行这个函数,毕竟子类已经继承了父类的方法了。如下:

class Singleton(object):
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, '_instance'):
            cls._instance = super().__new__(cls, *args, **kwargs)
        return cls._instance

class Foo(Singleton):
    pass

再测试一下:

a = Foo()
b = Foo()
print(id(a)==id(b))  # 得到 True

以上就是本篇教程的全部核心内容,谢谢您的阅读!

补充内容:

  1. 什么是元类?
    简单回答:对象的抽象化是类,而类的抽象化就是元类,或者说,对象是类的实例,类就是元类的实例,还可以说,类具体化后得到对象,元类具体化后得到类。
    具体回答:详见本博客的后续博文。

  2. 诸如以上提到的__new__()__call__()这类函数,它们存在的意义是什么?我们到底为什么需要它们?
    :以上这样的函数还有很多,它们最主要的用途就是帮助我们定制化我们自己的类,让类的功能更符合我们的需求。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 136,088评论 19 139
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 33,198评论 18 399
  • //Clojure入门教程: Clojure – Functional Programming for the J...
    葡萄喃喃呓语阅读 9,248评论 0 7
  • 问题1 表述以及解决方案 一个项目包含很多模块所有的模块依赖总pom文件。 这个项目属于spring-boot项目...
    eric_fun阅读 2,338评论 0 0
  • 最近总是和母亲吵架,可是我是真的爱她,她受的苦不知道为什么没有抵消我心中的怨,始终我不爱我自己,也不够...
    席媛阅读 1,625评论 0 0

友情链接更多精彩内容