文末有一些補(bǔ)充內(nèi)容岛琼,如果你不太理解本文中提到的這些函數(shù)的意義或者不知道元類是什么東西凤粗,可以先查看補(bǔ)充內(nèi)容,以幫助你做好理解本文所必要的知識(shí)儲(chǔ)備月趟。
1. 在進(jìn)入正題之前,我們需要先了解兩個(gè)有特殊用途的函數(shù)恢口,
__new__()
和__call__()
.
__new__()
它是在創(chuàng)建實(shí)例的時(shí)候被調(diào)用(注意此處的"實(shí)例"孝宗,我在這里并沒有說(shuō)"類的實(shí)例",因?yàn)槌祟惛纾€有元類因妇,元類創(chuàng)建實(shí)例的時(shí)候也會(huì)調(diào)用這個(gè)函數(shù))
__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, ...)
.
它是在“實(shí)例被當(dāng)成函數(shù)調(diào)用時(shí)”被調(diào)用。
舉個(gè)例子猿诸,實(shí)例如果是"Demo
"婚被,那么,當(dāng)你寫下"Demo()
"的時(shí)候梳虽,該實(shí)例(即Demo
)的創(chuàng)建者(注意:此處提到的創(chuàng)建者既有可能是類址芯,也有可能是元類)中的__call__()
被調(diào)用。如果這個(gè)實(shí)例是一個(gè)類窜觉,那么它的創(chuàng)建者就是一個(gè)元類是复,如果這個(gè)實(shí)例是一個(gè)對(duì)象,那么它的創(chuàng)建者就是一個(gè)類竖螃。
所以淑廊,要利用Python實(shí)現(xiàn)單例模式,我們也有兩種思路:
2. (下面是正題特咆,我們來(lái)談一談如何在Python3中實(shí)現(xiàn)單例模式):
第一種思路是利用元類季惩,元類的實(shí)例是類录粱,而類被當(dāng)成函數(shù)調(diào)用時(shí)不就是對(duì)象嗎?(假設(shè)類名是
Demo
画拾,則Demo()
就創(chuàng)建并返回了一個(gè)對(duì)象)因此啥繁,我們可以通過(guò)定制元類中的__call__()
來(lái)實(shí)現(xiàn)單例。
代碼如下:
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
"的實(shí)例旗闽,Foo()
就是元類"Singleton
"的實(shí)例被當(dāng)成函數(shù)在調(diào)用,因此元類"Singleton
"中的__call__()
就會(huì)被調(diào)用蜜另。我們輸入以下代碼來(lái)測(cè)試一下:
a = Foo()
b = Foo()
print(id(a)==id(b)) # 得到 True
第二種思路則不需要用到元類适室。
由于__new__()
是在創(chuàng)建實(shí)例的時(shí)候被調(diào)用(即,如果你創(chuàng)建一個(gè)類Foo
举瑰,并且這個(gè)Foo
中含有一個(gè)__new__()
的方法捣辆,那么該方法將在你創(chuàng)建這個(gè)Foo
類的對(duì)象時(shí)被調(diào)用,即在你寫Foo()
的時(shí)候被調(diào)用)此迅,所以我們可以通過(guò)直接定制__new__()
來(lái)實(shí)現(xiàn)單例汽畴。
代碼如下:
class Foo(object):
def __new__(cls, *args, **kwargs):
if not hasattr(cls, '_instance'):
cls._instance = super().__new__(cls, *args, **kwargs)
return cls._instance
測(cè)試一下:
a = Foo()
b = Foo()
print(id(a)==id(b)) # 得到 True
當(dāng)然,如果你不想在當(dāng)前這個(gè)類中定制
__new__()
方法耸序,你也可以在當(dāng)前類的父類中定制__new__()
忍些,python的解釋器依然能夠找到并正確執(zhí)行這個(gè)函數(shù),畢竟子類已經(jīng)繼承了父類的方法了坎怪。如下:
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
再測(cè)試一下:
a = Foo()
b = Foo()
print(id(a)==id(b)) # 得到 True
以上就是本篇教程的全部核心內(nèi)容罢坝,謝謝您的閱讀!
補(bǔ)充內(nèi)容:
什么是元類芋忿?
簡(jiǎn)單回答:對(duì)象的抽象化是類炸客,而類的抽象化就是元類疾棵,或者說(shuō)戈钢,對(duì)象是類的實(shí)例,類就是元類的實(shí)例是尔,還可以說(shuō)殉了,類具體化后得到對(duì)象,元類具體化后得到類拟枚。
具體回答:詳見本博客的后續(xù)博文薪铜。諸如以上提到的
__new__()
和__call__()
這類函數(shù),它們存在的意義是什么恩溅?我們到底為什么需要它們隔箍?
答:以上這樣的函數(shù)還有很多,它們最主要的用途就是幫助我們定制化我們自己的類脚乡,讓類的功能更符合我們的需求蜒滩。