日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

python元类单例_python面向对象和元类的理解

發布時間:2023/12/2 python 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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面向对象和元类的理解的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。