文末有一些补充内容,如果你不太理解本文中提到的这些函数的意义或者不知道元类是什么东西,可以先查看补充内容,以帮助你做好理解本文所必要的知识储备。
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 forx.__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
以上就是本篇教程的全部核心内容,谢谢您的阅读!
补充内容:
什么是元类?
简单回答:对象的抽象化是类,而类的抽象化就是元类,或者说,对象是类的实例,类就是元类的实例,还可以说,类具体化后得到对象,元类具体化后得到类。
具体回答:详见本博客的后续博文。诸如以上提到的
__new__()
和__call__()
这类函数,它们存在的意义是什么?我们到底为什么需要它们?
答:以上这样的函数还有很多,它们最主要的用途就是帮助我们定制化我们自己的类,让类的功能更符合我们的需求。