1. 程式人生 > 其它 >Python面向物件之反射、元類

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])