day24 反射\元类
生活随笔
收集整理的這篇文章主要介紹了
day24 反射\元类
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
反射 reflect
# 什么是反射, 其實(shí)是反省,自省的意思,反射指的是一個(gè)對象應(yīng)該具備,可以檢測,修改,增加自身屬性的能力. # 反射就是通過字符串操作屬性,涉及的四個(gè)函數(shù),這四個(gè)函數(shù)就是普通的內(nèi)置函數(shù),沒有雙下劃綫,與print等等沒有區(qū)別.案例
hasattr getattr setattr delattr p = Person("jack",18,"man")# if hasattr(p,"name"): # 1.判斷某個(gè)對象是否存在某個(gè)屬性 # print(getattr(p,"names",None)) # 2.從對象中取出屬性,第三個(gè)值位默認(rèn)值 當(dāng)屬性不存在是返回默認(rèn)值# 3.為對象添加新的屬性 setattr(p,"id","123") print(p.id)# 4.從對象中刪除屬性 delattr(p,"id") print(p.id)?使用場景:
# 反射其實(shí)就是對屬性的增刪改查,但是如果直接使用內(nèi)置的__dict__來操作,語法繁瑣,不好理解 # 另外一個(gè)最主要的問題是,如果對象不是我自己寫的是另一方提供的,我就必須判斷這個(gè)對象是否滿足的要求,也就是是否我需要的屬性和方法?
框架設(shè)計(jì)方式:
框架代碼:
# 反射被稱為框架的基石,為什么? 因?yàn)榭蚣艿脑O(shè)計(jì)者,不可能提前知道你的對象到底是怎么設(shè)計(jì)的,所以你提供給框架的對象 必須通過判斷驗(yàn)證之后才能正常使用,判斷驗(yàn)證就是反射要做的事情, 當(dāng)然通過__dict__也是可以實(shí)現(xiàn)的, 其實(shí)這些方法也就是對__dict__的操作進(jìn)行了封裝 需求:要實(shí)現(xiàn)一個(gè)用于處理用戶的終端指令的小框架,框架就是已經(jīng)實(shí)現(xiàn)了最基礎(chǔ)的構(gòu)架,就是所有項(xiàng)目都一樣的部分?
實(shí)例
import plugins# 框架已經(jīng)實(shí)現(xiàn)的部分 def run(plugin):while True:cmd = input("請輸入指令:")if cmd == "exit":break# 因?yàn)闊o法確定框架使用者是否傳入正確的對象所以需要使用反射來檢測# 判斷對象是否具備處理這個(gè)指令的方法if hasattr(plugin,cmd):# 取出對應(yīng)方法方法func = getattr(plugin,cmd)func() # 執(zhí)行方法處理指令else:print("該指令不受支持...")print("see you la la!")# 創(chuàng)建一個(gè)插件對象 調(diào)用框架來使用它 # wincmd = plugins.WinCMD() # 框架之外的部分就有自定義對象來完成 linux = plugins.LinuxCMD() run(linux)插件部分:
class WinCMD:def cd(self):print("wincmd 切換目錄....")def delete(self):print("wincmd 要不要刪庫跑路?")def dir(self):print("wincmd 列出所有文件....")class LinuxCMD:def cd(self):print("Linuxcmd 切換目錄....")def rm(self):print("Linuxcmd 要不要刪庫跑路?")def ls(self):print("Linuxcmd 列出所有文件....")上述框架代碼中,寫死了必須使用某個(gè)類,這是不合理的,因?yàn)闊o法提前知道對方的類在什么地方,以及類叫什么
所以我們應(yīng)該為框架的使用者提供一個(gè)配置文件,要求對方將累的信息寫入配置文件,然后框架自己去加載需要的模塊
框架代碼:
import importlib import settings# 框架已經(jīng)實(shí)現(xiàn)的部分 def run(plugin):while True:cmd = input("請輸入指令:")if cmd == "exit":break# 因?yàn)闊o法確定框架使用者是否傳入正確的對象所以需要使用反射來檢測# 判斷對象是否具備處理這個(gè)指令的方法if hasattr(plugin,cmd):# 取出對應(yīng)方法方法func = getattr(plugin,cmd)func() # 執(zhí)行方法處理指令else:print("該指令不受支持...")print("see you la la!")# 創(chuàng)建一個(gè)插件對象 調(diào)用框架來使用它 # wincmd = plugins.WinCMD() # 框架之外的部分就有自定義對象來完成# 框架 得根據(jù)配置文件拿到需要的類 path = settings.CLASS_PATH # 從配置中單獨(dú)拿出來 模塊路徑和 類名稱 module_path,class_name = path.rsplit(".",1) #拿到模塊 mk = importlib.import_module(module_path) # 拿到類 cls = getattr(mk,class_name) # 實(shí)例化對象 obj = cls() #調(diào)用框架 run(obj)如此一來,框架就與實(shí)現(xiàn)代碼徹底解耦了,只剩下配置文件
?
元類 metaclass?
# 元類是什么,用于創(chuàng)建類的類,萬物皆對象,類當(dāng)然也是對象 ,對象是通過類實(shí)例化產(chǎn)生的,如果類也是對象的話,必然類對象也是有另一個(gè)類實(shí)例化產(chǎn)生的,默認(rèn)情況下所有類的元類都是type.?
代碼
class Person:pass p = Person()print(type(p)) print(type(Person))Person類是通過type類實(shí)例化產(chǎn)生的學(xué)習(xí)元類的目的:
# 高度的自定義一個(gè)類,例如控制類的名字必須以大駝峰的方式來書寫 類也是對象,也有自己的類, 我們的需求是創(chuàng)建類對象做一些限制 想到了初始化方法 我們只要找到類對象的類(元類),覆蓋其中 init方法就能實(shí)現(xiàn)需求 當(dāng)然我們不能修改源代碼,所以應(yīng)該繼承type來編寫自己的元類,同時(shí)覆蓋init來完成需求?
運(yùn)用
""" 只要繼承了type 那么這個(gè)類就變成了一個(gè)元類 """ # 定義了一個(gè)元類 class MyType(type):def __init__(self,clss_name,bases,dict):super().__init__(clss_name,bases,dict)print(clss_name,bases,dict)if not clss_name.istitle():raise Exception("你丫的 類名不會寫...")# 為pig類指定了元類為MyType class Pig(metaclass=MyType):passclass Duck(metaclass=MyType):pass元類中call方法
# 當(dāng)你調(diào)用類對象時(shí)會自動珍惜元類中的__call__方法 ,并將這個(gè)類本身作為第一個(gè)參數(shù)傳入,以及后面的一堆參數(shù) 覆蓋元類中的call之后,這個(gè)類就無法產(chǎn)生對象,必須調(diào)用super().__call__來完成對象的創(chuàng)建 并返回其返回值使用場景:
當(dāng)你想要控制對象的創(chuàng)建過程時(shí),就覆蓋call方法 當(dāng)你想要控制類的創(chuàng)建過程時(shí),就覆蓋init方法?
案例:
實(shí)現(xiàn)將對象的所有屬性名稱轉(zhuǎn)為大寫
lass MyType(type):def __call__(self, *args, **kwargs):new_args = []for a in args:new_args.append(a.upper())print(new_args)print(kwargs)return super().__call__(*new_args,**kwargs)class Person(metaclass=MyType):def __init__(self,name,gender):self.name = nameself.gender = genderp = Person(name="jack",gender="woman") print(p.name) print(p.gender)注意:一旦覆蓋了call必須調(diào)用父類的call方法來產(chǎn)生對象并返回這個(gè)對象?
補(bǔ)充new方法
# 當(dāng)你要創(chuàng)建類對象時(shí),會首先執(zhí)行元類中的__new__方法,拿到一個(gè)空對象,然后會自動調(diào)用__init__來對這個(gè)類進(jìn)行初始化操作 注意:,如果你覆蓋了該方法則必須保證,new方法必須有返回值且必須是,對應(yīng)的類對象?
測試
class Meta(type):def __new__(cls, *args, **kwargs):print(cls) # 元類自己print(args) # 創(chuàng)建類需要的幾個(gè)參數(shù) 類名,基類,名稱空間print(kwargs) #空的 print("new run")# return super().__new__(cls,*args,**kwargs)obj = type.__new__(cls,*args,**kwargs)return objdef __init__(self,a,b,c):super().__init__(a,b,c)print("init run") class A(metaclass=Meta):pass print(A)?
總結(jié)new方法和init 都可以實(shí)現(xiàn)控制類的創(chuàng)建過程,init更簡單
單例設(shè)計(jì)模式
```
設(shè)計(jì)模式?用于解決某種固定問題的套路 例如:MVCMTV等 單例:指的是一個(gè)類產(chǎn)生一個(gè)對象 為什么要使用單例:單例是為了節(jié)省 資源,當(dāng)一個(gè)類的所有對象屬性全部相同時(shí),則沒有必要創(chuàng)建多個(gè)對象元類實(shí)現(xiàn):
# 單例n元類 class Single(type):def __call__(self, *args, **kwargs):if hasattr(self,"obj"): #判斷是否存在已經(jīng)有的對象return getattr(self,"obj") # 有就返回 obj = super().__call__(*args,**kwargs) # 沒有則創(chuàng)建print("new 了")self.obj = obj # 并存入類中return objclass Student(metaclass=Single):def __init__(self,name):self.name = nameclass Person(metaclass=Single):pass# 只會創(chuàng)建一個(gè)對象 Person() Person()?
?
?
aa
?
轉(zhuǎn)載于:https://www.cnblogs.com/Ryan-Yuan/p/11272612.html
總結(jié)
以上是生活随笔為你收集整理的day24 反射\元类的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python学习之数据类型(int,bo
- 下一篇: redis集群的搭建详细教程