反射 reflect
反射 reflect
什么是反射,其實就是反省,自省的意思
反射指的是以一個對象應該具備,可以檢測,修改,增加自身屬性的能力
反射就是通過字符串操作屬性
涉及到的四個函數,這四個函數就是普通的內置函數,沒有雙下下劃線,與print等等沒有區別
hasattr getattr setattr delattrp = Person("jack",18,"man")# if hasattr(p,"name"): # 1.判斷某個對象否存在某個屬性 # print(getattr(p,"name",None)) # 2.從對象中取出屬性,第二個值位默認值,當屬性不存在是返回默認值# 3.為對象增加新的屬性 setattr(p,"id","123") print(p.id) # 4.從對象中刪除屬性 delattr(p."id") print(p.id)使用場景:
反射其實就是對屬性的增刪改查,但是如果直接使用內置的dict來操作,語法繁瑣,不好理解
另外一個最主要的問題是,如果對象不是我自己寫的,是另一方提供的,我就必須判斷這個對象是否滿足的要求,也就是是否我需要的屬性和方法
框架設計方式:
框架代碼:
""" 反射被稱為框架的基石,為什么 因為框架的設計者,不可能提前知道你的對象到底是怎么設計的 所以你提供給框架的對象 必須得通過判斷驗證之后才能正常使用 判斷驗證就是反射要做的事情 當然通過__dict__也是可以是實現的,其實這些方法也就是對__dict__的操作進行了封裝 需求:要實現一個用于處理用戶的終端指令的小框架 框架就是已經實現了最基礎的架構,就是所有項目都一樣的部分 """import plugins# 框架已經實現的部分 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() # 框架之外的部分就有了自定義對象來完成 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 列出所有文件.....')上述框架代碼中,寫死了,必須使用某個類,這是不合理的,因為無法提前知道對方的類在什么地方,以及類叫什么
所有我們應該為框架的使用者提供一個配置文件,要求對方將類的信息寫入配置問件
然后框架自己去加載需要的模塊
最后的框架代碼:
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 = plugin.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)如此一來,框架就與實現代碼徹底解耦了,只剩下配置文件。
元類 metaclass
元類是什么,用于創建類的類
萬物皆都對象,類當然也是對象
對象是通過類實例化產生的,如果類也是對象的話,必然類對象也是有另一個類實例化產生的
默認情況下所有的類的元類都是type
驗證:
class Person:pass p = Person()print(type(p)) print(type(Person))Person類是通過type類實例化產生的學習元類的目的:
高度的自定義一個類,例如控制類的名字必須以大駝峰的方式來書寫
類也是對象,也有自己的類
我們的需求是創建對象做一些限制
想到了初始化方法 我們只要找到對象的類(元類),覆蓋其中init方法就能實現需求。
代碼:
""" 只要繼承了type 那么這個類就變成了一個元類 """ # 定義了一個元類 class MyType(type):def __init__(self,class_name,bases,dict):super().__init__(calss_name,bases,dict)print(class_name,bases,dict)if not class_name.istitle():rsise Exception("你丫的 類名都不會寫...")# 為pig類指定了元類為MyType class Pig(metaclass=MyType):passclass Duck(metaclass=MyType):pass元類中call方法
當你調用類對象時會自動執行元類中的__call__方法,并將這個類本身作為第一個參數傳入,以及后面的一推參數,覆蓋元類中的call之后,這個類就無法產生對象,必須調用super().__call__來完成對象的創建 并返回其返回值使用場景:
當你想要控制對象的創建過程時,就覆蓋call方法
當你想要控制類的創建過程時,就覆蓋init方法
案例:
實現將對象的所有屬性名稱轉為大寫
class 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 = name self.gender = gender p = Person(name="jack",gender="woman") print(p.name) print(p.gender)注意:一旦覆蓋了call必須調用父類的call方法來產生對象并返回這個對象
補充new方法
當你要創建類對象時,會首先執行元類中的__new__方法,拿到一個空對象,然后會自動調用__init__類來對這個類進行初始化操作 注意:如果你覆蓋了該方法則必須保證,new方法必須有返回值且必須是,對應的類對象測試:
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)總結new方和init都可以實現控制類的創建過程,init更簡單
單例設計模式
設計模式?用于解決某種固定問題的思路 例如:MVCMTV等 單例:指的是以一個類產生一個對象 為什么要使用單例:單例是為了節省資源,當一個類的所有對象屬性全部相同時,則沒有必要創建多個對象元類的實現:
# 單例n元類 class Single(type):def __init__(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 = name class Person(metaclass=Single):def __init__(self,name):self.name = name class Person(metaclass=Single):pass# 只會創建一個對象 Person() Person()冒泡算法
""" 冒泡排序 從小到大 第一圈: [2,1,3,5] 第一次 得出2的位置 [2,1,3,5] 第二次 [2,3,1,5] 第三次 [2,3,5,1]次數為 元素個數 -1 -(圈數索引為0)第二圈: [2,3,5,1] 第一次 [3,2,5,1] 第二次 [3,5,2,1] 第三次 [3,5,2,1]次數為 元素個數 -1 -(圈數索引為1)第三圈: [2,3,5,1] 第一次 [3,2,5,1] 第二次 [3,5,2,1]次數為: 元素個數 -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) """ 編寫student類,存儲用戶信息,要求改類對象可以使用點語法來操作屬性,也可以通過中括號的方式操作屬性 """ class Student(dict):def __init__(self,name,age,gender,team_name):super().__init__()self.name = nameself.age = ageself.gender = genderself.team_name = team_namedef __getattr__(self, item):return self[item]def __setattr__(self, key, value):self[key] = valuedef __delattr__(self, item):del self[item]def __gt__(self, other):return self.age > other.age """ 要求通過班級可以獲取到班級下的所有學員 ,思考關聯關系,應該如何表示 """ stu1 = Student("小雞",2,"woman",c1.name) # stu2 = Student("小鴨",1,"woman",c1.name) # stu3 = Student("小鵝",3,"woman","沒班級...")# stus = [stu1,stu2,stu3]# # res = [] # for stu in stus: # if stu.team_name == c1.name: # res.append(stu) # # # print(c1.name,"共有:",res) # 按照年齡從小到大 stus = [stu1,stu2,stu3]for i in range(len(stus)):for j in range(i,len(stus)-1):s1 = stus[j]s2 = stus[j+1]if s1 > s2:stus[j] = s2stus[j+1] = s1print("交換位置了!") print(stus)轉載于:https://www.cnblogs.com/zuihoudebieli/p/11285280.html
總結
以上是生活随笔為你收集整理的反射 reflect的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 学习旧岛小程序 (4)封装api 请求
- 下一篇: SQL错误提示档案(3):SQL Ser