深入了解python中元類的相關(guān)知識(shí)
類也是對(duì)象
在大多數(shù)編程語(yǔ)言中,類就是一組用來(lái)描述如何生成一個(gè)對(duì)象的代碼段,在python中也是成立的。
class ObjectCreator: pass my_object = ObjectCreator() print(my_object) """ 輸出結(jié)果: <__main__.ObjectCreator object at 0x037DACD0> """
但是,python的類不止于此,類同樣也是一種對(duì)象。
class ObjectCreator: pass
上面的代碼段將在內(nèi)存中創(chuàng)建一個(gè)對(duì)象,名字就叫做ObjectCreator。這個(gè)對(duì)象(類對(duì)象ObjectCreator)擁有創(chuàng)建對(duì)象(實(shí)例對(duì)象)的能力,但它本質(zhì)上仍然還是一個(gè)對(duì)象,于是你就可以對(duì)它做如下的操作:
- 給它復(fù)制一個(gè)變量
- 拷貝它
- 給它增加屬性
- 將它作為函數(shù)參數(shù)傳遞
示例代碼:
class ObjectCreator: pass # 把它賦值給一個(gè)變量 a = ObjectCreator print(a) # <class '__main__.ObjectCreator'> # 作為函數(shù)參數(shù)傳遞 def echo(o): print(o) echo(ObjectCreator) # <class '__main__.ObjectCreator'>
動(dòng)態(tài)的創(chuàng)建類
因?yàn)轭愐彩菍?duì)象,所以可以在運(yùn)行時(shí)動(dòng)態(tài)的創(chuàng)建它們,使用class關(guān)鍵字即可。
def choose_class(name): if name == 'foo': class Foo(object): pass return Foo # 返回的是類,不是類的實(shí)例 else: class Bar(object): pass return Bar MyClass = choose_class("foo") print(MyClass) # 打印類對(duì)象 # 輸出結(jié)果 <class '__main__.choose_class.<locals>.Foo'> print(MyClass()) # 打印實(shí)例對(duì)象 # 輸出結(jié)果 <__main__.choose_class.<locals>.Foo object at 0x0368CFD0>
使用type創(chuàng)建類
我們知道通過(guò)type()可以知道這個(gè)對(duì)象的類型是什么,他還有一個(gè)完全不同的功能,動(dòng)態(tài)的創(chuàng)建類。
type可以接受一個(gè)類的描述作為參數(shù),然后返回一個(gè)類。
語(yǔ)法:
type(類名,由父類名稱構(gòu)成的元組(針對(duì)繼承的情況可以為空),包含屬性的字典)
MyClass = type("MyClass",(),{}) print(MyClass) # 輸出結(jié)果: <class '__main__.MyClass'>
使用type創(chuàng)建帶屬性的類
type 接受一個(gè)字典來(lái)為類定義屬性,如下所示:
Foo = type("Foo",(),{'bar':True})
等價(jià)于
class Foo: bar = True
使用type創(chuàng)建繼承的子類
接著上面的代碼,我們已經(jīng)創(chuàng)建了一個(gè)Foo類,現(xiàn)在來(lái)創(chuàng)建一個(gè)它的子類。
FooChild = type("FooChild",(Foo,),{}) print(FooChild.bar) # # bar屬性是由Foo繼承而來(lái) # 輸出結(jié)果: True
注意:
- type的第二個(gè)參數(shù),元組中是父類的名字,不是字符串。
- 添加的屬性是類屬性,不是實(shí)例屬性。
使用type創(chuàng)建帶有方法的類
最終你會(huì)希望為你的類增加方法。只需要定義一個(gè)有著恰當(dāng)簽名的函數(shù)并將其作為屬性賦值就可以了。
添加實(shí)例方法
def test_f(self): print("添加的實(shí)例方法") Foo = type("Foo",(),{"test_f":test_f}) f = Foo() f.test_f() # 輸出結(jié)果: 添加的實(shí)例方法
添加靜態(tài)方法
@staticmethod def test_static(): print("添加的靜態(tài)方法") Foo = type("Foo",(),{"test_static":test_static}) Foo.test_static() Foo.test_static() # 輸出結(jié)果: 添加的靜態(tài)方法
添加類方法
@classmethod def test_class(cls): print("添加的類方法") Foo = type("Foo",(),{"test_class":test_class}) Foo.test_class() # 輸出的結(jié)果: 添加的類方法
什么是元類
元類就是用來(lái)創(chuàng)建類的“東西”。元類就是就是用來(lái)創(chuàng)建類對(duì)象的,元類就是類的類。
可以這樣理解:
MyClass = MetaClass() # 使用元類創(chuàng)建類對(duì)象 MyObject = MyClass() # 使用類對(duì)象創(chuàng)建實(shí)例對(duì)象
type函數(shù)其實(shí)就是元類。type就是在Python在背后創(chuàng)建所有類的元類,可以通過(guò)__class __屬性來(lái)查看,__class __的功能是查看對(duì)象所在的類,它可以嵌套使用。
class A: pass print(A.__class__) a = A() print(a.__class__) print(a.__class__.__class__) # 輸出結(jié)果: <class 'type'> <class '__main__.A'> <class 'type'>
可以看出,最后對(duì)象的類都是type元類。
Python中所有的東西,注意,我是指所有的東西——都是對(duì)象。這包括整數(shù)、字符串、函數(shù)以及類。它們?nèi)慷际菍?duì)象,而且它們都是從一個(gè)類創(chuàng)建而來(lái),這個(gè)類就是type。
整數(shù):
age = 18 print(age.__class__) print(age.__class__.__class__) # 輸出結(jié)果: <class 'int'> <class 'type'>
字符串:
name = "張三" print(name .__class__) print(name .__class__.__class__) # 輸出結(jié)果: <class 'str'> <class 'type'>
函數(shù):
def f(): pass print(f.__class__) print(f.__class__.__class__) # 輸出結(jié)果: <class 'function'> <class 'type'>
自定義元類
首先的了解一下metaclass屬性,用它來(lái)指定一個(gè)元類,python會(huì)在定義的類中尋找metaclass屬性,如果沒(méi)找到,就到它的父類找以此類推。如果找到了,python就會(huì)用它來(lái)創(chuàng)建類對(duì)象,如果實(shí)在沒(méi)有找到就會(huì)用內(nèi)建的type來(lái)創(chuàng)建這個(gè)類。
metaclass中可以放type或者任何使用到type或者子類化type的東東都可以。
自定義類的主要目的:
- 攔截類的創(chuàng)建
- 修改類
使用函數(shù)實(shí)現(xiàn)一個(gè)自定義的元類
功能:把不是__開頭的類屬性名字變?yōu)榇髮?br />
def upper_attr(future_class_name: str,future_class_parents: tuple,future_class_attr: dict): newAttr = {} for key,value in future_class_attr.items(): if not key.startswith("__"): newAttr[key.upper()] = value return type(future_class_name,future_class_parents,newAttr) class Foo(metaclass=upper_attr): name = "張三" age = 18 hasattr(Foo,"name") # 判斷是否有該類屬性 False hasattr(Foo,"NAME") # True hasattr(Foo,"age") # False hasattr(Foo,"AGE") # True
繼承type實(shí)現(xiàn)一個(gè)自定義元類
功能:同上
class MyMetaClass(type): def __new__(cls, class_name: str, class_parents: tuple, class_attr: dict): newAttr = {} for key, value in class_attr.items(): if not key.startswith("__"): newAttr[key.upper()] = value # 方法1:通過(guò)'type'來(lái)做類對(duì)象的創(chuàng)建 # return type(class_name, class_parents, newAttr) # 方法2:復(fù)用type.__new__方法 # 這就是基本的OOP編程,沒(méi)什么魔法 # return type.__new__(cls, class_name, class_parents, newAttr) # 方法3:使用super方法 return super(MyMetaClass, cls).__new__(cls, class_name, class_parents, newAttr) class Foo(metaclass=MyMetaClass): name = "張三" age = 18 hasattr(Foo,"name") # 判斷是否有該類屬性 False hasattr(Foo,"NAME") # True hasattr(Foo,"age") # False hasattr(Foo,"AGE") # True
效果和上面是一樣的。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Python入門教程 超詳細(xì)1小時(shí)學(xué)會(huì)Python
本文適合有經(jīng)驗(yàn)的程序員盡快進(jìn)入Python世界.特別地,如果你掌握J(rèn)ava和Javascript,不用1小時(shí)你就可以用Python快速流暢地寫有用的Python程序.2006-09-09Python3 把一個(gè)列表按指定數(shù)目分成多個(gè)列表的方式
今天小編就為大家分享一篇Python3 把一個(gè)列表按指定數(shù)目分成多個(gè)列表的方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-12-12Python使用內(nèi)置函數(shù)setattr設(shè)置對(duì)象的屬性值
這篇文章主要介紹了Python使用內(nèi)置函數(shù)setattr設(shè)置對(duì)象的屬性值,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10關(guān)于使用Python的time庫(kù)制作進(jìn)度條程序
這篇文章主要介紹了關(guān)于使用Python的time庫(kù)制作進(jìn)度條程序,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04Python headers請(qǐng)求頭如何實(shí)現(xiàn)快速添加
這篇文章主要介紹了Python headers請(qǐng)求頭如何實(shí)現(xiàn)快速添加,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11python根據(jù)出生年份簡(jiǎn)單計(jì)算生肖的方法
這篇文章主要介紹了python根據(jù)出生年份簡(jiǎn)單計(jì)算生肖的方法,通過(guò)一個(gè)非常簡(jiǎn)單的自定義函數(shù)實(shí)現(xiàn)輸入年份得到生肖的功能,非常實(shí)用,需要的朋友可以參考下2015-03-03