詳解python單例模式與metaclass
單例模式的實(shí)現(xiàn)方式
將類實(shí)例綁定到類變量上
class Singleton(object):
_instance = None
def __new__(cls, *args):
if not isinstance(cls._instance, cls):
cls._instance = super(Singleton, cls).__new__(cls, *args)
return cls._instance
但是子類在繼承后可以重寫__new__以失去單例特性
class D(Singleton):
def __new__(cls, *args):
return super(D, cls).__new__(cls, *args)
使用裝飾器實(shí)現(xiàn)
def singleton(_cls):
inst = {}
def getinstance(*args, **kwargs):
if _cls not in inst:
inst[_cls] = _cls(*args, **kwargs)
return inst[_cls]
return getinstance
@singleton
class MyClass(object):
pass
問(wèn)題是這樣裝飾以后返回的不是類而是函數(shù),當(dāng)然你可以singleton里定義一個(gè)類來(lái)解決問(wèn)題,但這樣就顯得很麻煩了
使用__metaclass__,這個(gè)方式最推薦
class Singleton(type):
_inst = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._inst:
cls._inst[cls] = super(Singleton, cls).__call__(*args)
return cls._inst[cls]
class MyClass(object):
__metaclass__ = Singleton
metaclass
元類就是用來(lái)創(chuàng)建類的東西,可以簡(jiǎn)單把元類稱為“類工廠”,類是元類的實(shí)例。type就是Python的內(nèi)建元類,type也是自己的元類,任何一個(gè)類
>>> type(MyClass) type >>> type(type) type
python在創(chuàng)建類MyClass的過(guò)程中,會(huì)在類的定義中尋找__metaclass__,如果存在則用其創(chuàng)建類MyClass,否則使用內(nèi)建的type來(lái)創(chuàng)建類。對(duì)于類有繼承的情況,如果當(dāng)前類沒(méi)有找到,會(huì)繼續(xù)在父類中尋找__metaclass__,直到所有父類中都沒(méi)有找到才使用type創(chuàng)建類。
如果模塊里有__metaclass__的全局變量的話,其中的類都將以其為元類,親自試了,沒(méi)這個(gè)作用,無(wú)任何影響
查看type的定義,
type(object) -> the object's type
type(name, bases, dict) -> a new type
所以利用type定義一個(gè)類的元類,可以用函數(shù)返回一個(gè)上面第二種定義的對(duì)象,也可以繼承type并重寫其中的方法。
直接使用type生成的對(duì)象作為元類,函數(shù)作用是使屬性變?yōu)榇髮?/p>
def update_(name, bases, dct):
attrs = ((name, value) for name, value in dct.items() if not name.startswith('__'))
uppercase_attr = {name.upper(): value for name, value in attrs}
return type(name, bases, uppercase_attr)
class Singleton(object):
__metaclass__ = update_
abc = 2
d = Singleton()
print d.ABC
# 2
上一節(jié)中,單例模式元類實(shí)現(xiàn)用的是類繼承方式,而對(duì)于第一種__new__的方式,本質(zhì)上調(diào)用的是type.__new__,不過(guò)使用super能使繼承更清晰一些并避免一些問(wèn)題
這里簡(jiǎn)單說(shuō)明一下,__new__是在__init__前調(diào)用的方法,會(huì)創(chuàng)建對(duì)象并返回,而__init__則是用傳入的參數(shù)將對(duì)象初始化??匆幌聇ype中這兩者以及__call__的實(shí)現(xiàn)
def __init__(cls, what, bases=None, dict=None): # known special case of type.__init__
"""
type(object) -> the object's type
type(name, bases, dict) -> a new type
# (copied from class doc)
"""
pass
@staticmethod # known case of __new__
def __new__(S, *more): # real signature unknown; restored from __doc__
""" T.__new__(S, ...) -> a new object with type S, a subtype of T """
pass
def __call__(self, *more): # real signature unknown; restored from __doc__
""" x.__call__(...) <==> x(...) """
pass
前面提到類相當(dāng)于元類的實(shí)例化,再聯(lián)系創(chuàng)建單例模式時(shí)使用的函數(shù),用的是__call__,其實(shí)用三種magic method中任何一種都是可以的,來(lái)看一下使用元類時(shí)各方法的調(diào)用情況
class Basic(type):
def __new__(cls, name, bases, newattrs):
print "new: %r %r %r %r" % (cls, name, bases, newattrs)
return super(Basic, cls).__new__(cls, name, bases, newattrs)
def __call__(self, *args):
print "call: %r %r" % (self, args)
return super(Basic, self).__call__(*args)
def __init__(cls, name, bases, newattrs):
print "init: %r %r %r %r" % (cls, name, bases, newattrs)
super(Basic, cls).__init__(name, bases, dict)
class Foo:
__metaclass__ = Basic
def __init__(self, *args, **kw):
print "init: %r %r %r" % (self, args, kw)
a = Foo('a')
b = Foo('b')
結(jié)果
new: <class '__main__.Basic'> 'Foo' () {'__module__': '__main__', '__metaclass__': <class '__main__.Basic'>, '__init__': <function init at 0x106fd5320>}
init: <class '__main__.Foo'> 'Foo' () {'__module__': '__main__', '__metaclass__': <class '__main__.Basic'>, '__init__': <function init at 0x106fd5320>}
call: <class '__main__.Foo'> ('a',)
init: <__main__.Foo object at 0x106fee990> ('a',) {}
call: <class '__main__.Foo'> ('b',)
init: <__main__.Foo object at 0x106feea50> ('b',) {}
元類的__init__和__new__只在創(chuàng)建類Foo調(diào)用了一次,而創(chuàng)建Foo的實(shí)例時(shí),每次都會(huì)調(diào)用元類的__call__方法
以上就是本文的全部?jī)?nèi)容,對(duì)python單例模式與metaclass進(jìn)行了描述,希望對(duì)大家的學(xué)習(xí)有所幫助。
相關(guān)文章
Python+tkinter實(shí)現(xiàn)制作文章搜索軟件
無(wú)聊的時(shí)候做了一個(gè)搜索文章的軟件,有沒(méi)有更加的方便快捷不知道,好玩就行了。軟件是利用Python和tkinter實(shí)現(xiàn)的,感興趣的可以嘗試一下2022-10-10
Python3加密解密庫(kù)Crypto的RSA加解密和簽名/驗(yàn)簽實(shí)現(xiàn)方法實(shí)例
這篇文章主要介紹了Python3加密解密庫(kù)Crypto的RSA加解密和簽名/驗(yàn)簽實(shí)現(xiàn)方法實(shí)例,需要的朋友可以參考下2020-02-02
為Python的web框架編寫MVC配置來(lái)使其運(yùn)行的教程
這篇文章主要介紹了為Python的web框架編寫MVC配置來(lái)使其運(yùn)行的教程,示例代碼基于Python2.x版本,需要的朋友可以參考下2015-04-04
pyecharts調(diào)整圖例與各板塊的位置間距實(shí)例
這篇文章主要介紹了pyecharts調(diào)整圖例與各板塊的位置間距實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-05-05
python 如何獲取頁(yè)面所有a標(biāo)簽下href的值
這篇文章主要介紹了python 獲取頁(yè)面所有a標(biāo)簽下href的值操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-05-05
Python實(shí)現(xiàn)對(duì)照片中的人臉進(jìn)行顏值預(yù)測(cè)
今天給大家?guī)?lái)的是關(guān)于Python實(shí)戰(zhàn)的相關(guān)知識(shí),文章圍繞如何用Python實(shí)現(xiàn)對(duì)照片中的人臉進(jìn)行顏值預(yù)測(cè)展開(kāi),文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下2021-06-06

