Python面向物件之反射、元類
一、反射
反射指的是一個物件應該具備,可以增、刪、改、查屬性的能力,通過字串來操作屬性。涉及四個函式,這四個函式就是普通的內建函式,沒有下劃線,但實現的功能和原理基本一致
hasattr(object,name) # 判斷物件是否實現某個屬性,返回值是bool型別 setattr(object,name,value) # 為物件增加新的屬性 getattr(object,name,default) # 從物件中獲取某個屬性,返回值是str型別 delattr(object,) # 從物件中刪除某個屬性,無返回值
class Person:def __init__(self,name,age,gender): self.name=name self.age=age self.gender=gender p=Person("aaa",20,"woman") # print(p.name) print(hasattr(p,"name")) # 判斷是否是類的屬性 True if hasattr(p,"name"): print("我有name") print(getattr(p,"name",None)) #從物件中取出屬性,第三個值位預設值 aaa #當屬性不存在是返回預設值 # 為物件新增新的屬性 a = setattr(p,"id","1234") print(a) # 從物件中刪除物件 c = delattr(p,"id") print(c)
反射使用:
1、一個類在定義的時候,可能一些屬性的設計並不是很完美,而後期需要作出修改過或刪除操作屬性時,使用反射可以不需要修改原始碼。反射其實就是對屬性的增刪改查,但如果直接使用內建的__dict__來操作,語法繁瑣不便操作。
2、另一個就是呼叫另一方提供的物件時,必須先判斷這個物件是否滿足需求,也就是判斷是否是我們需要的屬性和方法。動態新增模組功能。框架就與程式碼實現了徹底的耦合
二、元類 metaclass
元類是建立類的類 所有的物件都是例項化或者說呼叫類而得到的(呼叫類的過程稱為類的例項化)。元類即 用於產生類的類。預設情況下所有類的元類都是type
1、編寫元類:
只需要宣告一個繼承自type的類(使用class關鍵字)
類只是物件,元類也只是類。元類的行為繼承自type;因此任何type的子類都可以作為元類
例項:例如控制類的名字必須以大駝峰體的方式來書寫。用初始化的方法 我們只要找到類物件的類(元類),覆蓋其中__ 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",(),{})
2、元類中的__call__方法
當呼叫類物件時會自動執行元類中的__call__方法,並將這個類本身作為第一個引數傳入,以及後面的數,覆蓋元類中的__call__之後,這個類無法產生物件,必須呼叫super().__call__來完成物件的建立,並返回其返回值。
#實現將物件的所有屬性名稱轉化為大寫 class MyType(type): def __call__(self, *args, **kwargs): new_arg = [] # 要求的書寫規範 for a in kwargs: new_arg.append(a.upper()) # 轉換為大寫 print(new_arg) #print(kwargs) return super().__call__(*new_arg) class Person(metaclass=MyType): def __init__(self,name,gender): self.name=name self.gender=gender p=Person(name="aaa",gender="man") print(p.gender) print(p.name) ''' ['NAME', 'GENDER'] GENDER NAME '''
3、__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) print(obj) #<class '__main__.A'> return obj #無返回值,不執行init方法 def __init__(self,a,b,c): super().__init__(a,b,c) print("init run") #有返回值就執行init run class A(metaclass=Meta): pass print(A) #此時有返回值執行init方法,就執行這個 ''' <class '__main__.Meta'> ('A', (), {'__module__': '__main__', '__qualname__': 'A'}) {} new run init run <class '__main__.A'> '''
4、單例
單例是指的是單個例項,指一個類只能有一個例項物件。為了節省資源,當兩個物件的資料完全相同時 則沒有必要佔用兩份資源。
# 單例n元類 class Single(type): def __call__(self, *args, **kwargs): if hasattr(self,"obj"): #判斷是否存在已經有的物件 return getattr(self,"obj") # 有就返回 obj = super().__call__(*args,**kwargs) # 沒有則建立 print("這下有了") self.obj = obj # 並存入類中 return obj class Student(metaclass=Single): def __init__(self,name): self.name = name class Person(metaclass=Single): pass # 只會建立一個物件 Person() Person()
四、冒泡演算法:氣泡排序
氣泡排序(Bubble Sort)也是一種簡單直觀的排序演算法。它重複地走訪過要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來。走訪數列的工作是重複地進行直到沒有再需要交換,也就是說該數列已經排序完成。這個演算法的名字由來是因為越小的元素會經由交換慢慢"浮"到數列的頂端。
def bubbleSort(arr): n = len(arr) # 遍歷所有陣列元素 for i in range(n): print(i) for j in range(0, n - i - 1): if arr[j] > arr[j + 1]: arr[j], arr[j + 1] = arr[j + 1], arr[j] arr = [64, 34, 25, 12, 22, 11, 90] #即用64與34比較,交換二者位置,再與25比較又交換位置,以此類推 bubbleSort(arr) print("排序後的陣列:") for i in range(len(arr)): print("%d" % arr[i])