python元类单例_python面向对象和元类的理解
1 python類對象與實例對象
python中一切皆對象(廣義上的對象),類也不例外,我們可以稱類為類對象。python中內建函數type()可以返回對象的類型,例如type(int)會得到返回值,而int為類型工廠函數,是內置的類對象。如果對自定義的類使用type()也會返回相同的值。
從上述我們可以總結出:python中type為一般類對象的類(除非子類化type,用type子類創建類對象),即type類可以實例化得到類對象,再由類對象實例化得到實例對象。創建類的類被稱為元類
1.1 類屬性&實例屬性
1.1.1 類屬性
類屬性是一個類對象的數據或函數元素,類屬性包括數據屬性和方法。
數據屬性 是僅屬于類的變量,與任何實例無關,這與C++和Java中靜態成員變量相似。
方法 是定義在類中的函數,根據用法,可以分為綁定方法與非綁定方法。綁定方法必須實例化后才能調用,需要與實例綁定;非綁定方法則可以分為靜態方法和類方法,可以使用裝飾器@staticmethod和@classmethod實現。雖然綁定方法需要實例化后才能調用,但其定義在類中,因此其也屬于類的屬性。
class P():
version = 1.0 #類的靜態數據
@classmethod
def show(cls): #類方法
print(cls.version)
def __init__(self, name, address): #綁定方法,self代表實例
self.name = name
self.address = address
def display(self):
print(self.name)
print(self.address)
def __call__(self):
print(self.name)
def fn():
print('hello,python')
if __name__ == '__main__':
p = P('jay','american')
p1 = P('h','china')
p1.fn = fn
print(P.__dict__)
print(p1.__dict__)
以上是創建類對象的一個例子,__dict__為對象的特殊屬性,其值為對象屬性構成的字典(并非所有類或實例都有該屬性,若定義了__slots__屬性則會取代__dict__屬性),
運行結果如下:
>>
{'__module__': '__main__', 'version': '1.0',
'show': , '__init__': , 'display': , '__call__': , '__dict__': , '__weakref__': , '__doc__': None}
{'name': 'h', 'address': 'china', 'fn': }
運行結果同時也驗證了上述說法,類中定義的綁定方法:__init__, display, __call__,和非綁定方法:show,都是屬于類的屬性;而fn,name,address則屬于實例屬性。
1.1.2 實例屬性
實例屬性是只屬于實例對象的方法和數據屬性。不同于靜態語言,python能動態地創建實例屬性,上述代碼中的fn即為動態創建的實例屬性。
但這樣特性的缺陷是如果實例屬性在條件語句中創建,若該條件語句并未執行,該屬性也就不存在,而后面的代碼試著訪問這些屬性,就會發生錯誤。
解決辦法是避免中途創建實例屬性,盡量在__init__方法中初始化實例屬性。
1.1.3 類屬性VS實例屬性
類屬性也能通過實例訪問,但前提是該實例中沒有同名的屬性。任何對實例屬性的賦值都會新建一個實例屬性并對其賦值,但如果類屬性中存在同名的屬性,則不會對類屬性產生影響,若要改變類屬性需要使用類名,而不是實例名。
class P():
version = 1
>>p1 = P()
>>p1.version
1
>>p1.version = 2
>>p1.version
2
>>P.version
1
>>P.version = 2
>>P.version
2
1.2 類的特殊方法(魔術方法)
__init__:初始化方法,屬于類的綁定方法,第一個參數必須為self(代表實例對象),且無返回值,在__new__返回實例對象后才能調用。
__new__:真正的構造方法,用于創建實例對象,屬于靜態方法,第一個參數為cls(代表類對象),其返回值為實例對象。
__call__:將對象設置為可調用對象(可以向函數一樣用()調用),元類type中實現了該函數,因此類對象為可調用對象,實例化時就是通過調用類來創建實例對象的,覆蓋時需要調用父類方法,即super().__call__()
關鍵知識:覆蓋上述特殊函數時,需要特別注意,元類中自定義__call__(self, *args, **kwargs),類對象中若自定義__init__(self, *args, **kwargs), 則自定義__new__(cls, *args, **kwargs),原因如下:
p1 = P(*args, **kwargs)
'''執行順序:'''
P.__call__(*args, **kwargs):
super().__call__(*args, **kwargs)
goto↓
p1 = object().__new__(P)
p1.__init__(*args, **kwargs)
return p1
2 Metaclass元類
2.1 自定義元類
元類是用來創建其他的類,它的實例就是其他的類。
執行類定義時,解釋器會先尋找類屬性__metaclass__,如果此屬性中未定義,則向上查找其父類中的__metaclass__
type的__init__函數有三個位置參數,分別為:類名稱,父類名稱元祖,類屬性字典;可直接使用type()快速創建類對象:
People = type('People',(object,),dict(show = fn, setage = fn1, age = None))
也可子類化type類后,創建元類,再創建類對象:
from time import ctime
class MetaC(type):
def __init__(self, *args):
super().__init__(*args)
print('調用類的init')
def __call__(self, *args, **kwargs):
print('調用類的call')
_instance = super().__call__(*args, **kwargs)
print('call return %s' %_instance)
return _instance
class Foo(metaclass=MetaC):
# __metaclass__ = MetaC
def __init__(self, version=None):
print('調用對象的init')
def __new__(cls, *args, **kwargs):
print('調用對象的new')
_instance = super().__new__(cls)
print('new return %s' %_instance)
return _instance
foo = Foo('hello')
運行結果:
>>
調用類的init
調用類的call
調用對象的new
new return <__main__.foo object at>
調用對象的init
call return <__main__.foo object at>
2.2 單例模式的實現
下面是幾種單例模式的實現方法,有些會用到上述的元類知識。
2.2.1 裝飾器實現單例
import weakref
def single_obj(cls):
#實例緩存為弱引用,實例不被使用時會釋放該實例
_spam_cache = weakref.WeakValueDictionary()
@wraps(cls)
def wrapper(*args, **kwargs):
if cls not in _spam_cache :
_instance = cls(*args, **kwargs)
_spam_cache [cls] = _instance
return _instance
else:
return _spam_cache [cls]
return wrapper
@single_obj
class A():
def __init__(self, version):
self.version = version
print(self.version)
a1 = A(1.3)
a2 = A(1.2)
print('a1 id:',id(a1))
print('a2 id:',id(a2))
2.2.2 利用元類,類的__call__方法實現單例
class Singleton(type):
def __init__(self, *args, **kwargs):
self._instance = None
super().__init__(*args, **kwargs)
def __call__(self, *args, **kwargs):
if self._instance is None:
self._instance = super().__call__(*args, **kwargs)
return self._instance
else:
return self._instance
class B(metaclass=Singleton):
def __init__(self, name):
self.name = name
print(name)
b1 = B('what')
b2 = B('hell')
print(b2.name)
print('b1 id:',id(b1))
print('b2 id:',id(b2))
2.2.3 通過__new__實現單例(此方法不可行)
class C(object):
_instance = None
def __init__(self, name):
self.name = name
print(name)
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
c1 = C('dsfsfd')
c2 = C('java')
print('c1 id:',id(c1))
print('c2 id:',id(c2))
若采用上述方法,無論類是否實例化,__init__都會被調用,實例對象會被修改;
總結
以上是生活随笔為你收集整理的python元类单例_python面向对象和元类的理解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python中用于标识字符串的定界符_P
- 下一篇: python漏洞检测脚本_URL重定向漏