反射、元类
一、反射
1、什么是反射:就是反省,自省的意思
反射指的是一個對象應該具備,可以增、刪、改、查屬性的能力,通過字符串來操作屬性
涉及的四個函數,這四個函數就是普通的內置函數,只是沒有下劃線而已,實現的功能和原理基本一致
hasattr(object,name) # 判斷對象是否實現某個屬性 setattr(object,name,value) # 為對象增加新的屬性 getattr(object,name,default) # 從對象中獲取某個屬性 delattr(object,) # 從對象中刪除某個屬性 # 反射 class Person:def __init__(self,name,age,gender):self.name=nameself.age=ageself.gender=gender p=Person("James",20,"woman") # print(p.name) print(hasattr(p,"name")) # 判斷是否是類的屬性 if hasattr(p,"name"):print(getattr(p,"name",None)) #從對象中取出屬性,第三個值位默認值# 當屬性不存在是返回默認值# 為對象添加新的屬性 setattr(p,"id","1234") print(p,id)# 從對象中刪除對象 delattr(p,"id") print(p,id)>>
True
James
<__main__.Person object at 0x0000018DC46D5E10> <built-in function id>
?
2、為什么需要反射?
一個類在定義的時候,可能一些屬性的設計并不是很完美,而后期需要作出修改過或刪除操作屬性時,使用 反射可以不需要修改源代碼
3、反射的優勢:可插拔設計(重點)
使用的場景:反射其實就是對屬性的增刪改查,但如果直接使用內置的__dict__來操作,語法繁瑣不便操作
另一個點就是調用另一方提供的對象時,必須先判斷這個對象是否滿足需求,也就是判斷是否是我們需要的屬性和方法。
動態添加模塊功能
""" 反射被稱為框架的基石,為什么 因為框架的設計者,不可能提前知道你的對象到底是怎么設計的 所以你提供給框架的對象 必須通過判斷驗證之后才能正常使用 判斷驗證就是反射要做的事情, 當然通過__dict__也是可以實現的, 其實這些方法也就是對__dict__的操作進行了封裝 需求:要實現一個用于處理用戶的終端指令的小框架 框架就是已經實現了最基礎的構架,就是所有項目都一樣的部分 """import importlib import settings# 框架已經實現的部分 def run(plugin):while True:cmd = input("請輸入指令:")if cmd == "exit":break# 因為無法確定框架使用者是否傳入正確的對象所以需要使用反射來檢測# 判斷對象是否具備處理這個指令的方法if hasattr(plugin,cmd):# 取出對應方法方法func = getattr(plugin,cmd)func() # 執行方法處理指令else:print("該指令不受支持...")print("see you la la!")# 創建一個插件對象 調用框架來使用它 # wincmd = plugins.WinCMD() # 框架之外的部分就有自定義對象來完成# 框架 得根據配置文件拿到需要的類 path = settings.CLASS_PATH # 從配置中單獨拿出來 模塊路徑和 類名稱 module_path,class_name = path.rsplit(".",1) #拿到模塊 mk = importlib.import_module(module_path) # 拿到類 cls = getattr(mk,class_name) # 實例化對象 obj = cls() #調用框架 run(obj) 框架代碼 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 列出所有文件....") 插件(調用)
如此一來,框架就與代碼實現了徹底的耦合,只的剩下配置文件
其實就是當調用其他外界導入的類的功能和方法的時候,是否使用符合當前需求的操作時,用hasattr做一個判斷,再用getattr獲取屬性的方法
二、元類:metaclass
元類是創建類的類? 所有的對象都是實例化或者說調用類而得到的(調用類的過程稱為類的實例化)
class創建一個類應該細分為四個過程:
1.獲取類名
2.獲取基類
3.獲取名稱空間
4.實例化元類得到類
總結:元類即 用于產生類的類
?
2、自定義元類控制類的創建:?
通過type高度定義一個類,例如要求所有的方法名稱必須小寫,類名稱必須大寫開頭等
class MyMate(type):#關鍵字typedef __init__(self,name,bases,dic):print("run")if not dic.get("__doc__"):raise TypeError("類必須有文檔注釋!")if not name.istitle():raise TypeError("類名必須大寫開頭!")super().__init__(name,bases,dic) class Foo(object,metaclass=MyMate): #綁定元類pass3、默認情況下所有類的元類都是type
驗證:
class Person:pass p=Person() print(type(p)) print(type(Person))>> <class '__main__.Person'> <class 'type'> # Person的類型是type?4、學習元類的目的:高度的自定義一個類,例如控制類的名字必須以大駝峰體的方式來書寫、
?
想到了初始化方法? 我們只要找到類對象的類(元類),覆蓋其中__ init__方法就能實現需求
當然我們不能修改源代碼,所以應該繼承type來編寫自己的元類,同時覆蓋__init__來完成需求
# 定義一個元類 class MyType(type):def __init__(self,clss_name,bases,dict): #繼承后用super調用super().__init__(clss_name,bases,dict)print(clss_name,bases,dict)if not clss_name.istitle():raise Exception("類名寫錯了~") class pig (metaclass=MyType):print("綁定了元類~") class Duck(metaclass=MyType):print("規定的協議要遵守~")MyType("pig",(),{})?5、元類中調用__call__方法
當你調用類對象時會自動執行元類中的__call__方法,并將這個類本身作為第一個參數傳入,以及后面的數,
覆蓋元類中的__call__之后,這個類無法產生對象,必須調用super().__call__來完成對象的創建,并返回其返回值。
實現將對象的所有屬性名稱傳為大寫:
實現將對象的所有屬性名稱轉化為大寫 class MyType(type):def __call__(self, *args, **kwargs):new_arg = [] # 要求的書寫規范for a in args:new_arg.append(a.upper()) # 轉換為大寫print(new_arg)print(kwargs)return super().__call__(*new_arg,**kwargs) class Person(metaclass=MyType):def __init__(self,name,gender):self.name=nameself.gender=genderp=Person(name="jack",gender="man") print(p.gender) print(p.name)>>[] {'name': 'jack', 'gender': 'woman'} jack woman?注意:一旦覆蓋了call必須調用父類的call方法來產生對象并返回這個對象
?6、__new__的方法:
當你創建類的對象時,會先執行元類中的__new__ 方法
執行了__new__函數,就不會再執行__init__,因為__new__函數是真正用于創建類的方法,只有創建類成功了才會執行__init__函數,__new__必須要有返回值且返回值類型為__type__時才會執行__init__函數,
?
class Meta(type):def __new__(cls, *args, **kwargs):print(cls) # 元類自己print(args) # 創建類需要的幾個參數 類名,基類,名稱空間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)?
?
7、元類實現單例(單例設計模式)
什么是單例?
單例是指的是單個實例,指一個類只能有一個實例對象
為什么要使用單例?
為了節省資源,當兩個對象的數據完全相同時 則沒有必要占用兩份資源
# 單例n元類 class Single(type):def __call__(self, *args, **kwargs):if hasattr(self,"obj"): #判斷是否存在已經有的對象return getattr(self,"obj") # 有就返回 obj = super().__call__(*args,**kwargs) # 沒有則創建print("new 了")self.obj = obj # 并存入類中return objclass Student(metaclass=Single):def __init__(self,name):self.name = nameclass Person(metaclass=Single):pass# 只會創建一個對象 Person() Person()?
冒泡算法:
總結規律 圈數 是元素個數減一 次數 元素個數 - 1 - (圈數索引) 我們需要兩層循環 一層控制圈數 一層控制次數 """ ls = [2,1,3,5,100,24,12,12,1,2,1,1,4,32] for i in range(len(ls)-1):for j in range(len(ls)-1-i):# 如果前面的小于后面的則交換位置if ls[j] > ls[j+1]:ls[j],ls[j+1] = ls[j+1],ls[j] print(ls)?
?
?
?
?
?
?
?
?
?
轉載于:https://www.cnblogs.com/Gaimo/p/11272011.html
總結
- 上一篇: 孕妇梦到自己被水淹了怎么回事
- 下一篇: 梦到好几个人一起生孩子