Python進階-----描述符(__get__(),__set__(),__delete__())


一、描述符是什么
  描述符本質就是一個新式類,在這個新式類中,至少實現了__get__(),__set__(),__delete__()中的一個,這也被稱為描述符協議
  __get__():調用一個屬性時,觸發
  __set__():為一個屬性賦值時,觸發
  __delete__():采用del刪除屬性時,觸發

1 class Foo: #在python3中Foo是新式類,它實現了三種方法,這個類就被稱作一個描述符
2     def __get__(self, instance, owner):
3         print('__get__(),被執行了')
4     def __set__(self, instance, value):
5         print('__set__(),被執行了')
6     def __delete__(self, instance):
7         print('__delete__(),被執行了')

二、描述符的作用
  描述符的作用是用來代理另外一個類的屬性的(必須把描述符定義成這個類的類屬性,不能定義到構造函數中)

1 class Test:
2     x = Foo()
3     def __init__(self,x):
4         self.x = x
5 
6 t = Test(2)      #'__set__(),被執行了'
7 print(t.x)         #'__get__(),被執行了'   'None'

三、描述符分為兩種
  1、數據描述符:至少實現了__get__()和__set__()
  2、非數據描述符:沒有實現__set__()

 1 #數據描述符
 2 class Foo:
 3     def __set__(self, instance, value):
 4         print('set')
 5     def __get__(self, instance, owner):
 6         print('get')
 7 
 8 #非數據描述符
 9 class Foo:
10     def __get__(self, instance, owner):
11         print('get')

四、注意事項:
  描述符本身應該定義成新式類,被代理的類也應該是新式類
  必須把描述符定義成這個類的類屬性,不能為定義到構造函數中
  要嚴格遵循該優先級,優先級由高到底分別是
    1類屬性
    2數據描述符
    3實例屬性
    4非數據描述符
    5找不到的屬性觸發__getattr__()

五、描述符的應用
  1、現在有一個需求,定義一個用戶信息,用戶的名字為字符串類型,年齡為int類型,收入為float類型,可以用描述符來代理這些屬性,
從而控制傳入的數據類型。

 1 # 定義描述符
 2 class Desc_type:
 3     def __get__(self, instance, owner):
 4         print('執行了__get__')
 5         print('instance是 %s'%instance)            #instance表示的是被代理的類屬性的類實例化出的對象,這里是p1
 6         print('owner是 %s'%owner)                #owner表示的是被代理的類,這里是People這個類
 7     def __set__(self, instance, value):
 8         print('執行了__set__')
 9         print('instance是 %s'%instance)            #instance表示的是被代理的類屬性的類實例化出的對象,這里是p1
10         print('value是 %s'%value)                #value表示的是被代理的類的屬性的值,這里是'Menawey'
11         print('self=====%s'%self)               #self表示的是描述符實例的對象Desc_type()--->name
12     def __delete__(self, instance):
13         print('執行了__delete__')
14         print('instance是 %s' % instance)
15 
16 # 定義一個人的類(被代理的類)
17 class People:
18     name = Desc_type()            #用描述符代理了name這個屬性
19     def __init__(self,name,age,salary):
20         self.name = name
21         self.age = age
22         self.salary = salary
23 
24 p1 = People('Meanwey',24,11.1)
25 print(p1.name)                    #會出發__get__
26 print(p1.__dict__)                 #{'age': 24, 'salary': 11.1}

發現被代理的name屬性並沒有被設置對應的值,所以__dict__中沒有'name',那是因為實例化的時候執行了__init__,所以也執行了__set__,
但是在__set__中並沒有真正的操作進行設置

  2、所以要想真正的對屬性進行代理,對屬性進行設置、獲取和刪除值,則需要通過操作底層__dict__字典,如下:

 1 # 定義描述符
 2 class Desc_type:
 3     def __init__(self,key,value_type):             #傳入key用來操作底層屬性字典,value_type用來表示期望的數據類型
 4         self.key = key
 5         self.value_type = value_type
 6     def __get__(self, instance, owner):
 7         print('執行了__get__')
 8         return instance.__dict__[self.key]              #return p2.name
 9     def __set__(self, instance, value):
10         print('執行了__set__',self)
11         if not isinstance(value,str):                   #用來判斷用戶傳入的是否符合要求
12             raise TypeError('%s 傳入的不是 %s'%(self.key,self.value_type))      #拋出類型異常,提示用戶程序終止
13         instance.__dict__[self.key] = value             #符合要求,則設置屬性對應的值
14     def __delete__(self, instance):
15         print('執行了__delete__')
16         instance.__dict__.pop(self.key)
17 
18 # 定義一個人的類(被代理的類)
19 class People:
20     name = Desc_type('name',str)  # 用描述符代理了name這個屬性,相當於執行了Desc_type中的self.__set__
21     age = Desc_type('age',int)
22     salary = Desc_type('salary',float)
23     def __init__(self, name, age, salary):
24         self.name = name
25         self.age = age
26         self.salary = salary
27 p2 = People('Meanwey',24,11.1)
28 
29 #訪問
30 print(p2.name)
31 
32 #賦值
33 p2.name = 'Jery'
34 p2.age = 18.1           #沒有傳入整型的數據,則 ----TypeError: age 傳入的不是 <class 'int'>

六、總結
  1、描述符就是一個類(新式類);
  2、描述符分為數據描述和非數據描述符,區別在於前者有__set__方法,后者沒有;
  3、描述符的使用要遵循優先級:類屬性>數據描述符>實例屬性>非數據描述符>找不到(__getattr__);
  4、描述符方法中的self表示的是描述符實例化的對象,instance表示的是被描述(代理)的類實例化的對象,owner表示的是被描述(代理)的類,value表示的是設置到被描述(代理)屬性的值。


免責聲明!

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



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