python - 數據描述符(class 內置 get/set/delete方法 )


數據描述符(class 內置 get/set/del方法 ):

# 什么是描述符
# 官方的定義:描述符是一種具有“捆綁行為”的對象屬性。訪問(獲取、設置和刪除)它的屬性時,實際是調用特殊的方法(_get_(),
# _set_(),_delete_())。也就是說,如果一個對象定義了這三種方法的任何一種,它就是一個描述符。
# 更多的理解:
# 通常情況下,訪問一個對象的搜索鏈是怎樣的?比如a.x,首先,應該是查詢 a._dict_[‘x’],然后是type(a)._dict_[‘x’],一直
# 向上知道元類那層止(不包括元類)。如果這個屬性是一個描述符呢?那python就會“攔截”這個搜索鏈,取而代之調用描述符方法
# (_get_)。

# 描述符有什么作用?
# The default behavior for attribute access is to get, set, or delete the attribute from an object's dictionary.
# For instance, a.x has a lookup chain starting witha.__dict__[‘x'], then type(a).__dict__[‘x'], and continuing
# through the base classes of type(a) excluding metaclasses. If the looked-up value is an object defining one of
# the descriptor methods, then Python may override the default behavior and invoke the descriptor method instead.
# Where this occurs in the precedence chain depends on which descriptor methods were defined.
#                                                                                               —–摘自官方文檔
# 簡單的說描述符會改變一個屬性的基本的獲取、設置和刪除方式。

# 數據描述符(data descriptor)和非數據描述符(non-data descriptors)
# 數據描述符:定義了_set_ 和_get_方法的對象
# 非數據描述符:只定義了_get_方法的對象。通常方法都是非數據描述符。

# 區別:
# 1、位於搜索鏈上的順序。搜索鏈(或者優先鏈)的順序大概是這樣的:數據描述符>實體屬性(存儲在實體的_dict_中)>非數據描述符。
# 這個順序初看起來挺晦澀。解釋如下:
# 獲取一個屬性的時候:
# 首先,看這個屬性是不是一個數據描述符,如果是,就直接執行描述符的_get_,並返回值。
# 其次,如果這個屬性不是數據描述符,那就按常規去從_dict_里面取。
# 最后,如果_dict_里面還沒有,但這是一個非數據描述符,則執行非數據描述符的_get_方法,並返回。

# 三個方法(協議):
# • _get_(self, instance, owner) —獲取屬性時調用,返回設置的屬性值,通常是_set_中的value,或者附加的其他組合值。
# • _set_(self, instance, value) — 設置屬性時調用,返回None.
# • _delete_(self, instance) — 刪除屬性時調用,返回None
# 其中,instance是這個描述符屬性所在的類的實體,而owner是描述符所在的類。

# 為什么要區分數據描述符和非數據描述符?(訪問屬性的優先級不一樣)
# 1.非數據描述符的獲取屬性的優先級鏈是,__getattribute__->找__dict__->找描述符,這條鏈的規則給了"緩存屬性"理論支持(通常是這種)
# 2.數據描述符,又分為:
#     2.1 有__get__方法的覆蓋型的獲取屬性的優先級鏈是,__getattribute__->找描述符,同時__set__方法也會覆蓋對實例屬性的賦值
# 操作,就是說任何外部對屬性的賦值都將被__set__捕獲,同時獲取屬性也是通過__get__方法獲取,__dict__不再起直接作用。
#     2.2 沒有__get__方法的覆蓋型的獲取屬性優先級鏈是在沒有對屬性賦值時是__getattribute__->找描述符(返回描述符對象本身)
# ,對屬性賦值了之后是__getattribute__->找__dict__->找描述符

# 對象屬性的訪問順序:
# ①.實例屬性
# ②.類屬性
# ③.父類屬性
# ④.__getattr__()方法

#數據描述符 - 優先級順序:
# ① __getattribute__(), 無條件調用
# ② 數據描述符:由 ① 觸發調用 (若人為的重載了該 __getattribute__() 方法,可能會調職無法調用描述符)
# ③ 實例對象的字典(若與描述符對象同名,會被覆蓋哦)
# ④ 類的字典
# ⑤ 非數據描述符
# ⑥ 父類的字典
# ⑦ __getattr__() 方法

# 描述符有什么用和好處
# 1)一般情況下不會用到,建議:先定基本的,以后真有需要再擴展。別貪玩。
# 2)可以在設置屬性時,做些檢測等方面的處理
# 3)緩存?
# 4)設置屬性不能被刪除?那定義_delete_方法,並raise 異常。
# 5)還可以設置只讀屬性
# 6)把一個描述符作為某個對象的屬性。這個屬性要更改,比如增加判斷,或變得更復雜的時候,所有的處理只要在描述符中操作就行了。

# #舉例:
# @property
# #這就是一個數據描述符
# class Afff():
#     pass

# 更詳盡的示例:
#摘錄於:
#描述符闡述:
# https://blog.csdn.net/allenalex/article/details/54097319
#應用示例:
# https://www.jb51.net/article/91028.htm
#優先級闡述:
# https://www.cnblogs.com/Jimmy1988/p/6808237.html

 示例:

#描述符類(相當於代理)
#定義為了數據描述符:定義了_set_ 和_get_方法的對象
class Foo():
    def __get__(self, instance, owner):
        print("執行Foo get方法")
    def __set__(self, instance, value):
        print("執行Foo set方法")
    def __delete__(self):
        print("執行Foo del方法")

#主要運行的類:
class Test():
    #類的x屬性被Foo代理,所以屬性訪問優先級也被修改:
    #類屬性 > 數據描述符 > 實例屬性 > 非實例屬性 > __getattr__()
    x = Foo()
    def __init__(self,num):
        self.x = num

#因為x類屬性被Foo代理,觸發Foo的set方法,而set方法只是打印了,沒有做數據操作,
abc = Test(100)
abc.x
print(abc.__dict__)

# print輸出:-----------
# 執行Foo set方法
# 執行Foo get方法
# {}
# 執行Foo del方法
# print輸出:-----------

 練習:

#描述符應用- 練習(判斷錄入類型是否合規)
class Test():
    def __init__(self, key,type_data):
        self.key = key
        self.type_data = type_data
    def __set__(self, instance, value):
        if isinstance(value,self.type_data):
            instance.__dict__[self.key] = value
        else:
            print("賦值類型錯誤")

class People():
    name =Test('name',str)
    def __init__(self,name,old):
        self.name = name
        self.old = old

#賦值為str類型
name = 'anec'
#賦值為int類型
name2 = 123

abc = People(name,23)
abc2 = People(name2,24)

print(abc.__dict__)
print(abc2.__dict__)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM