14 內置方法(中)之描述符

来源:https://www.cnblogs.com/oceanicstar/archive/2018/04/16/8852418.html
-Advertisement-
Play Games

面向對象學習目錄 1 面向對象介紹 2 類、實例、屬性、方法詳解 3 面向過程與面向對象進一步比較 4 類與對象 5 屬性查找與綁定方法 6 小結 7 繼承與派生 8 組合 9 抽象類 10 多態 11 封裝 12 綁定方法與非綁定方法 13 內置方法(上) 14 內置方法(中)之描述符 15 內置 ...


面向對象學習目錄

1 面向對象介紹

2 類、實例、屬性、方法詳解

3 面向過程與面向對象進一步比較

4 類與對象

5 屬性查找與綁定方法

6 小結

7 繼承與派生

8 組合

9 抽象類

10 多態

11 封裝

12 綁定方法與非綁定方法

13 內置方法(上)

14 內置方法(中)之描述符

15 內置方法(下)


六、描述符(__get__,__set__,__delete__) 1 描述符是什麼:         描述符本質就是一個新式類,在這個新式類中,至少實現了__get__(),__set__(),__delete__()中的一個,這也被稱為描述符協議         __get__():調用一個屬性時,觸發          __set__():為一個屬性賦值時,觸發          __delete__():採用del刪除屬性時,觸發           定義一個描述符
1 class Foo: # 在python3中Foo是新式類,它實現了三種方法,這個類就被稱作一個描述符
2     def __get__(self, instance, owner):
3         pass
4     def __set__(self, instance, value):
5         pass
6     def __delete__(self, instance):
7         pass

 

2 描述符是乾什麼的:         描述符的作用是用來代理另外一個類的屬性的(必須把描述符定義成這個類的類屬性,不能定義到構造函數中)         引子:描述符類產生的實例進行屬性操作並不會觸發三個方法的執行
 1 class 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')
 8  
 9 #包含這三個方法的新式類稱為描述符,由這個類產生的實例進行屬性的調用/賦值/刪除,並不會觸發這三個方法
10 f1=Foo()
11 f1.name='egon'
12 f1.name
13 del f1.name
14 #疑問:何時,何地,會觸發這三個方法的執行

 

描述符應用之何時?何地?
 1 # 描述符Str
 2 class Str:
 3     def __get__(self, instance, owner):
 4         print('Str調用')
 5     def __set__(self, instance, value):
 6         print('Str設置...')
 7     def __delete__(self, instance):
 8         print('Str刪除...')
 9  
10 # 描述符Int
11 class Int:
12     def __get__(self, instance, owner):
13         print('Int調用')
14     def __set__(self, instance, value):
15         print('Int設置...')
16     def __delete__(self, instance):
17         print('Int刪除...')
18  
19 class People:
20     name=Str()
21     age=Int()
22     def __init__(self,name,age): #name被Str類代理,age被Int類代理,
23         self.name=name
24         self.age=age
25  
26 # 何地?:定義成另外一個類的類屬性
27  
28 # 何時?:且看下列演示
29  
30 p1=People('alex',18)
31  
32 # 描述符Str的使用
33 p1.name
34 p1.name='egon'
35 del p1.name
36  
37 # 描述符Int的使用
38 p1.age
39 p1.age=18
40 del p1.age
41  
42 # 我們來瞅瞅到底發生了什麼
43 print(p1.__dict__)
44 print(People.__dict__)
45  
46 # 補充
47 print(type(p1) == People) #type(obj)其實是查看obj是由哪個類實例化來的
48 print(type(p1).__dict__ == People.__dict__)

 

3 描述符分兩種  一 數據描述符:         至少實現了__get__()和__set__()
1 class Foo:
2     def __set__(self, instance, value):
3         print('set')
4     def __get__(self, instance, owner):
5         print('get')

 

二 非數據描述符:         沒有實現__set__()
1 class Foo:
2     def __get__(self, instance, owner):
3         print('get')

 

4 註意事項:          一 描述符本身應該定義成新式類,被代理的類也應該是新式類          二 必須把描述符定義成這個類的類屬性,不能為定義到構造函數中          三 要嚴格遵循該優先順序,優先順序由高到底分別是                  1.類屬性                  2.數據描述符                  3.實例屬性                  4.非數據描述符                  5.找不到的屬性觸發__getattr__()   類屬性>數據描述符
 1 #描述符Str
 2 class Str:
 3     def __get__(self, instance, owner):
 4         print('Str調用')
 5     def __set__(self, instance, value):
 6         print('Str設置...')
 7     def __delete__(self, instance):
 8         print('Str刪除...')
 9  
10 class People:
11     name=Str()
12     def __init__(self,name,age): #name被Str類代理,age被Int類代理,
13         self.name=name
14         self.age=age
15  
16  
17 #基於上面的演示,我們已經知道,在一個類中定義描述符它就是一個類屬性,存在於類的屬性字典中,而不是實例的屬性字典
18  
19 #那既然描述符被定義成了一個類屬性,直接通過類名也一定可以調用吧,沒錯
20 People.name #恩,調用類屬性name,本質就是在調用描述符Str,觸發了__get__()
21  
22 People.name='egon' #那賦值呢,我去,並沒有觸發__set__()
23 del People.name #趕緊試試del,我去,也沒有觸發__delete__()
24 #結論:描述符對類沒有作用-------->傻逼到家的結論
25  
26 '''
27 原因:描述符在使用時被定義成另外一個類的類屬性,因而類屬性比二次加工的描述符偽裝而來的類屬性有更高的優先順序
28 People.name #恩,調用類屬性name,找不到就去找描述符偽裝的類屬性name,觸發了__get__()
29  
30 People.name='egon' #那賦值呢,直接賦值了一個類屬性,它擁有更高的優先順序,相當於覆蓋了描述符,肯定不會觸發描述符的__set__()
31 del People.name #同上
32 '''

數據描述符>實例屬性

 1 #描述符Str
 2 class Str:
 3     def __get__(self, instance, owner):
 4         print('Str調用')
 5     def __set__(self, instance, value):
 6         print('Str設置...')
 7     def __delete__(self, instance):
 8         print('Str刪除...')
 9  
10 class People:
11     name=Str()
12     def __init__(self,name,age): #name被Str類代理,age被Int類代理,
13         self.name=name
14         self.age=age
15  
16  
17 p1=People('egon',18)
18  
19 #如果描述符是一個數據描述符(即有__get__又有__set__),那麼p1.name的調用與賦值都是觸發描述符的操作,於p1本身無關了,相當於覆蓋了實例的屬性
20 p1.name='egonnnnnn'
21 p1.name
22 print(p1.__dict__)#實例的屬性字典中沒有name,因為name是一個數據描述符,優先順序高於實例屬性,查看/賦值/刪除都是跟描述符有關,與實例無關了
23 del p1.name

實例屬性>非數據描述符

 1 class Foo:
 2     def func(self):
 3         print('我胡漢三又回來了')
 4  
 5 f1=Foo()
 6 f1.func()
 7 #調用類的方法,也可以說是調用非數據描述符
 8 #函數是一個非數據描述符對象(一切皆對象麽)
 9 print(dir(Foo.func))
10 print(hasattr(Foo.func,'__set__'))
11 print(hasattr(Foo.func,'__get__'))
12 print(hasattr(Foo.func,'__delete__'))
13 #有人可能會問,描述符不都是類麽,函數怎麼算也應該是一個對象啊,怎麼就是描述符了
14 #笨蛋哥,描述符是類沒問題,描述符在應用的時候不都是實例化成一個類屬性麽
15 #函數就是一個由非描述符類實例化得到的對象
16 #沒錯,字元串也一樣
17  
18 f1.func='這是實例屬性啊'
19 print(f1.func)
20  
21 del f1.func #刪掉了非數據
22 f1.func()

再次驗證:實例屬性>非數據描述符

 1 class Foo:
 2     def __set__(self, instance, value):
 3         print('set')
 4     def __get__(self, instance, owner):
 5         print('get')
 6  
 7 class Room:
 8     name=Foo()
 9     def __init__(self,name,width,length):
10         self.name=name
11         self.width=width
12         self.length=length
13  
14  
15 #name是一個數據描述符,因為name=Foo()而Foo實現了get和set方法,因而比實例屬性有更高的優先順序
16 #對實例的屬性操作,觸發的都是描述符的
17 r1=Room('廁所',1,1)
18 r1.name
19 r1.name='廚房'
 1 class Foo:
 2     def __get__(self, instance, owner):
 3         print('get')
 4  
 5 class Room:
 6     name=Foo()
 7     def __init__(self,name,width,length):
 8         self.name=name
 9         self.width=width
10         self.length=length
11  
12  
13 #name是一個非數據描述符,因為name=Foo()而Foo沒有實現set方法,因而比實例屬性有更低的優先順序
14 #對實例的屬性操作,觸發的都是實例自己的
15 r1=Room('廁所',1,1)
16 r1.name
17 r1.name='廚房'

非數據描述符>找不到

1 class Foo:
2   def func(self):
3     print('我胡漢三又回來了')
4  
5   def __getattr__(self, item):
6     print('找不到了當然是來找我啦',item)
7 
8 f1=Foo()
9 f1.xxxxxxxxxxx

 

5 描述符使用         眾所周知,python是弱類型語言,即參數的賦值沒有類型限制,下麵我們通過描述符機制來實現類型限制功能   牛刀小試
 1 class Str:
 2     def __init__(self,name):
 3         self.name=name
 4     def __get__(self, instance, owner):
 5         print('get--->',instance,owner)
 6         return instance.__dict__[self.name]
 7     def __set__(self, instance, value):
 8         print('set--->',instance,value)
 9         instance.__dict__[self.name]=value
10     def __delete__(self, instance):
11         print('delete--->',instance)
12         instance.__dict__.pop(self.name)
13  
14  
15 class People:
16     name=Str('name')
17     def __init__(self,name,age,salary):
18         self.name=name
19         self.age=age
20         self.salary=salary
21  
22 p1=People('egon',18,3231.3)
23  
24 #調用
25 print(p1.__dict__)
26 p1.name
27  
28 #賦值
29 print(p1.__dict__)
30 p1.name='egonlin'
31 print(p1.__dict__)
32  
33 #刪除
34 print(p1.__dict__)
35 del p1.name
36 print(p1.__dict__)

拔刀相助

 1 class Str:
 2     def __init__(self,name):
 3         self.name=name
 4     def __get__(self, instance, owner):
 5         print('get--->',instance,owner)
 6         return instance.__dict__[self.name]
 7  
 8     def __set__(self, instance, value):
 9         print('set--->',instance,value)
10         instance.__dict__[self.name]=value
11     def __delete__(self, instance):
12         print('delete--->',instance)
13         instance.__dict__.pop(self.name)
14  
15  
16 class People:
17     name=Str('name')
18     def __init__(self,name,age,salary):
19         self.name=name
20         self.age=age
21         self.salary=salary
22  
23 #疑問:如果我用類名去操作屬性呢
24 People.name #報錯,錯誤的根源在於類去操作屬性時,會把None傳給instance

  修訂__get__方法

 1 class Str:
 2     def __init__(self,name):
 3         self.name=name
 4     def __get__(self, instance, owner):
 5         print('get--->',instance,owner)
 6         if instance is None:
 7             return self
 8         return instance.__dict__[self.name]
 9  
10     def __set__(self, instance, value):
11         print('set--->',instance,value)
12         instance.__dict__[self.name]=value
13     def __delete__(self, instance):
14         print('delete--->',instance)
15         instance.__dict__.pop(self.name)
16  
17  
18 class People:
19     name=Str('name')
20     def __init__(self,name,age,salary):
21         self.name=name
22         self.age=age
23         self.salary=salary
24         print(People.name) #完美,解決

磨刀霍霍

 1 class Str:
 2     def __init__(self,name,expected_type):
 3         self.name=name
 4         self.expected_type=expected_type
 5     def __get__(self, instance, owner):
 6         print('get--->',instance,owner)
 7         if instance is None:
 8             return self
 9             return instance.__dict__[self.name]
10  
11     def __set__(self, instance, value):
12         print('set--->',instance,value)
13         if not isinstance(value,self.expected_type): #如果不是期望的類型,則拋出異常
14             raise TypeError('Expected %s' %str(self.expected_type))
15             instance.__dict__[self.name]=value
16     def __delete__(self, instance):
17         print('delete--->',instance)
18         instance.__dict__.pop(self.name)
19  
20  
21 class People:
22     name=Str('name',str) #新增類型限制str
23     def __init__(self,name,age,salary):
24         self.name=name
25         self.age=age
26         self.salary=salary
27  
28 p1=People(123,18,3333.3) #傳入的name因不是字元串類型而拋出異常

大刀闊斧

 1 class Typed:
 2     def __init__(self,name,expected_type):
 3         self.name=name
 4         self.expected_type=expected_type
 5     def __get__(self, instance, owner):
 6         print('get--->',instance,owner)
 7         if instance is None:
 8             return self
 9         return instance.__dict__[self.name]
10  
11     def __set__(self, instance, value):
12         print('set--->',instance,value)
13         if not isinstance(value,self.expected_type):
14             raise TypeError('Expected %s' %str(self.expected_type))
15         instance.__dict__[self.name]=value
16     def __delete__(self, instance):
17         print('delete--->',instance)
18         instance.__dict__.pop(self.name)
19  
20  
21 class People:
22     name=Typed('name',str)
23     age=Typed('name',int)
24     salary=Typed('name',float)
25     def __init__(self,name,age,salary):
26         self.name=name
27         self.age=age
28         self.salary=salary
29  
30 p1=People(123,18,3333.3)
31 p1=People('egon','18',3333.3)
32 p1=People('egon',18,3333)

  大刀闊斧之後我們已然能實現功能了,但是問題是,如果我們的類有很多屬性,你仍然採用在定義一堆類屬性的方式去實現,low,這時候我需要教你一招:獨孤九劍

  類的裝飾器:無參
 1 def decorate(cls):
 2     print('類的裝飾器開始運行啦------>')
 3     return cls
 4  
 5 @decorate  # 無參:People=decorate(People)
 6 class People:
 7     def __init__(self,name,age,salary):
 8         self.name=name
 9         self.age=age
10         self.salary=salary
11  
12 p1=People('egon',18,3333.3)

類的裝飾器:有參

def typeassert(**kwargs):
    def decorate(cls):
        print('類的裝飾器開始運行啦------>',kwargs)
        return cls
    return decorate
 
@typeassert(name=str,age=int,salary=float) 
#有參: 1.運行typeassert(...)返回結果是decorate,此時參數都傳給kwargs 
#     2.People=decorate(People)
class People:
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary
 
p1=People('egon',18,3333.3)

終極大招

刀光劍影
 1 class Typed:
 2     def __init__(self,name,expected_type):
 3         self.name=name
 4         self.expected_type=expected_type
 5     def __get__(self, instance, owner):
 6         print('get--->',instance,owner)
 7         if instance is None:
 8             return self
 9         return instance.__dict__[self.name]
10  
11     def __set__(self, instance, value):
12         print('set--->',instance,value)
13         if not isinstance(value,self.expected_type):
14             raise TypeError('Expected %s' %str(self.expected_type))
15         instance.__dict__[self.name]=value
16     def __delete__(self, instance):
17         print('delete--->',instance)
18         instance.__dict__.pop(self.name)
19  
20 def typeassert(**kwargs):
21     def decorate(cls):
22         print('類的裝飾器

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 使用靜態關鍵字實現單例模式 單例模式:只能獲得某個類的唯一一個實例 單例模式,不管什麼時間點得到的對象都是同一個對象 看下麵代碼: 將構造方法私有,以便實現外部無法使用new進行實例化的效果,達到任何時候其實都是同一個對象的效果 測試代碼如下: 結果如下: 該結果表明:single、single2、 ...
  • 最小生成樹 下圖標明瞭六個城市(A~F)之間的公路(每條公路旁標註了其長度公裡數)。為將部分公路改造成高速公路,使各個城市之間均通過高速公路通達,至少要改造共計()公裡的公路,這種總公裡數最少的改造方案共有()個。 解析: (1)普里姆演算法 任取一點,例如A,將其納入已完成部分。點A與其他各點中的最... ...
  • Web應用中常用的HTTP方法有四種: 1.PUT方法用來添加的資源 2.GET方法用來獲取已有的資源 3.POST方法用來對資源進行狀態轉換 4.DELETE方法用來刪除已有的資源 Spring MVC最新的版本中提供了一種更加簡潔的配置HTTP方法的方式,增加了四個標註: @PutMapping ...
  • 1、page作用域也是最小的作用域,它只能在當前頁面中使用。 2、request作用域主要是發送請求,但只能在兩個頁面之間發送一次請求。 3、session作用域是一個會話,也就是一個瀏覽器,意思是說只能在當前會話中傳值,如果換個瀏覽器就不行了。 4、application作用域,這個就牛逼了,它是 ...
  • 手把手教你寫網路爬蟲(2) 作者:拓海 摘要:從零開始寫爬蟲,初學者的速成指南! 封面: 介紹 大家好!回顧上一期,我們在介紹了爬蟲的基本概念之後,就利用各種工具橫衝直撞的完成了一個小爬蟲,目的就是猛、糙、快,方便初學者上手,建立信心。對於有一定基礎的讀者,請不要著急,以後我們會學習主流的開源框架, ...
  • MyBatis 簡介 MyBatis 本是apache的一個開源項目iBatis, 2010年這個項目由apache software foundation 遷移到了google code,並且改名為MyBatis,是一個基於Java的持久層框架。 持久層: 可以將業務數據 存儲到磁碟,具備長期存儲 ...
  • 面向對象學習目錄 1 面向對象介紹 2 類、實例、屬性、方法詳解 3 面向過程與面向對象進一步比較 4 類與對象 5 屬性查找與綁定方法 6 小結 7 繼承與派生 8 組合 9 抽象類 10 多態 11 封裝 12 綁定方法與非綁定方法 13 內置方法(上) 14 內置方法(中)之描述符 15 內置 ...
  • 一直很想做cuda GPU編程,很早就將CUDA9.0安裝好了,後面就沒怎麼管它,忙別的去了。敲黑板,劃重點,我科研還是很努力的,可是很多人看不見罷了。之前一直在使用粒子方法進行流體模擬,計算時間極其漫長,周末想了想,自己來做一個大型顯式動力學分析軟體,學學CUDA編程是不錯的。所以現在為大家呈上熱 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...