python中的 descriptor

學好和用好python, descriptor是必須跨越過去的一個點,現在雖然Python書籍花樣百出,但是似乎都是在介紹一些Python庫而已,對Python語言本身的關注很少,或者即使關注了,但是能夠介紹把 dscriptor介紹清楚的,是很少的,到目前,我自己還沒有見到過。

一個attr能被稱為descriptor,除了需要定義?descriptor protocol 規定的方法外,這個attr必須是屬于某個class的,不能是屬于某個instance

一、Python中的descriptor

  在一個Python class 中重寫下面任何一個方法都稱為descriptor

    1.__get__(self,obj,type=None)---->value

    2.__set__(self,obj,value)---->None

    3.__delete__(self,obj)---->None

  descriptor細分:

    ?1.Data descriptor :      只是重寫__get__,__set__的class

     2.None Data descriptor:   ?只是重寫了__get__的class

     3.read-only Data descriptor ? ? 同時定義了__get__,__set__,但是這個__set__只是raise AttributeError

  Data descriptor和None Data descriptor 的區別:相對于 instance 字典的優先級。?

? ? ? ? ? ?若實例字典中有與描述器同名的屬性,若描述器為資料描述器,則優先訪問資料描述器;若描述器為非資料描述器,

? ? ? ? ? ?則優先使用字典中的屬性。這條規則在實際應用中的例子:如果實例中有方法和屬性重名時,Python會優先使用實例字典中的屬性,

? ? ? ? ? ?因為實例函數的實現是個非資料描述器。

?


二、通過instance訪問屬性:

  1.獲取attr

  instance.a

__getattribute__,__getattr__,__get__和__dict__都與屬性訪問有關,它們的優先級:

1.當類中( type(instance) )定義了__getattribute__方法時,無條件的調用__getattribute__.所以在__getattribute__方法中,不能出現self.__attr__這種調用,它會引起無限制遞歸

2.如果訪問的attr存在,并且這個attr是屬于 type(instance)的或者屬于type(instace) 的某個父類(是super class 不是metaclass)的,并且這個attr是一個descriptor那么,此時會轉而繼續調用都相應 class.__get__。 簡而言之:

  2.1 這個attr是個Descriptor,是調用這個屬性的__get__

2.2這個attr不是一個Descriptor,就調用__dict__[attr]

3.如果類中沒有定義該屬性,則調用__getattr__

4.否則,拋出異常AttributeError

?

  • 實驗一 : 在self.__dict__可以獲得某個遵守了descriptor的attr,這個attr不是一個descriptor,所以不遵守descriptor規則
class DataDescriptor(object):def __get__(self,obj,owner):print("datadescriptor.__get__ ",self,obj,owner)return 2class A(object):passclass B(A):def __init__(self):self.datadescriptor=DataDescriptor()a=B()
print a.datadescriptor
#輸出<__main__.DataDescriptor object at 0x00BD8DB0>
  • 實驗二:在class.__dict__中得到attr,并且這個attr是一個descriptor
class DataDescriptor(object):def __get__(self,obj,owner):print("datadescriptor.__get__ ",self,obj,owner)return 2class A(object):datadescriptor=DataDescriptor()class B(A):def __init__(self):passa=B()
print a.datadescriptor
'''
輸出 ('datadescriptor.__get__ ', <__main__.DataDescriptor object at 0x00BD8CF0>, <__main__.B object at 0x00BD8D50>,<class '__main__.B'>)
'''
  • 實驗三:__getattribute__返回非descriptor

?

class DataDescriptor(object):def __get__(self,obj,owner):print("DataDescriptor.__get__ ",self,obj,owner)return 2class A(object):datadescriptor=DataDescriptor()class B(A):def __init__(self):passdef __getattribute__(self,name):print("B.__getattribute__  name=",name)return "abc"a=B()
print a.datadescriptor
'''
輸出:('B.__getattribute__  name=', 'datadescriptor')
abc'''

 

  • 實驗四: __getattribute__返回descriptor,遵守descriptor規則
	def __get__(self,obj,owner):print("DataDescriptor.__get__ ",self,obj,owner)return 2class A(object):datadescriptor=DataDescriptor()class B(A):def __init__(self):passdef __getattribute__(self,name):print("B.__getattribute__  name=",name)return type(self).datadescriptora=B()
print a.datadescriptor'''
輸出:
('B.__getattribute__  name=', 'datadescriptor')
('DataDescriptor.__get__ ', <__main__.DataDescriptor object at 0x00BD8CB0>, None, <class '__main__.B'>)
2
'''
  • 實驗五,在找不到attr的情況下

這種情況比較特殊,在__getattribute__中return None 或者 沒有return 語句,都不會調用,只有 在__getattribute__中 raise?AttributeError(),才會調用 __getattr__,如果沒有定義__getattribute__ ,在找不到attribute的情況下,VM默認是會raise?AttributeError()的.

?代碼1

class DataDescriptor(object):def __get__(self,obj,owner):print("DataDescriptor.__get__ ",self,obj,owner)return 2class A(object):datadescriptor=DataDescriptor()class B(A):def __init__(self):passdef __getattribute__(self,name):print("B.__getattribute__  name=",name)raise AttributeError()#return Nonedef __getattr__(self,name):print("B.__getattr__ name=",name)return "Not Found"a=B()
print a.datadescriptor
'''
定義了__getattribute__,但是 raise AttributeError了,所以會轉而繼續調用到__getattr__,沒有沒有 raise AttributeError,無論__getattribute__中做了什么,都不會繼續調用__getattr__
'''

  代碼2

class DataDescriptor(object):def __get__(self,obj,owner):print("DataDescriptor.__get__ ",self,obj,owner)return 2class A(object):datadescriptor=DataDescriptor()class B(A):def __init__(self):pass#def __getattribute__(self,name):#	print("B.__getattribute__  name=",name)#	raise AttributeError()#return Nonedef __getattr__(self,name):print("B.__getattr__ name=",name)return "Not Found"a=B()
print a.zz
'''
找不到zz 這個attr,vm默認會 raise AttributeError,自動轉而調用__getattr__
'''

  2.設置instance.attr?

  設置instance.attr=value時,涉及到三個方法,分別為__setattr__、__set__和__dict__[attr]=val,沒有__setattribute__

?  ?調用的優先級為:

  1.如果type(instance) 中定義了__setattr__方法,就直接調用這個方法。

  2.如果這個attr是個descriptor,那會分情況:

    2.1,如果是個data descriptor(定義了 __set__方法),那么會調用 data descriptor的__set__方法

    2.2,如果是個None data descriptor(沒有定義__set__方法),那么會是instance.__dict__[attr]=value

  3.如果attr不是descriptor,會直接instance.__dict__[attr]=value

?  實驗一:None data descriptor時的設置

  

# -*- coding:utf-8 -*- class DataDescriptor(object):def __init__(self):self.values={};def __get__(self,obj,owner):print("DataDescriptor.__get__ ",self,obj,owner)return self.valuesclass A(object):datadescriptor=DataDescriptor()class B(A):def __init__(self):passa=B()
a.datadescriptor=999
print a.__dict__'''
輸出:
{'datadescriptor': 999}
'''

  實驗二:Data descriptor時的set attr

# -*- coding:utf-8 -*- class DataDescriptor(object):def __init__(self):self.values={};def __get__(self,obj,owner):print("DataDescriptor.__get__ ",self,obj,owner)return self.valuesdef __set__(self,instance,value):print("DataDescriptor.__set__ ",instance,value)class A(object):datadescriptor=DataDescriptor()class B(A):def __init__(self):passa=B()
a.datadescriptor=999
print a.__dict__'''
輸出:
('DataDescriptor.__set__ ', <__main__.B object at 0x00BD8E30>, 999)
{}
'''

  可以看出在data descriptor時,設置相應的data descriptor attribute時,沒有影響到instance.__dict__

  實驗三:type(instance)有定義__setattr__方法時:

  

# -*- coding:utf-8 -*- class DataDescriptor(object):def __init__(self):self.values={};def __get__(self,obj,owner):print("DataDescriptor.__get__ ",self,obj,owner)return self.valuesdef __set__(self,instance,value):print("DataDescriptor.__set__ ",instance,value)class A(object):datadescriptor=DataDescriptor()class B(A):def __init__(self):passdef __setattr__(self,key,value):print("B.__setattr__ ",key,value)self.__dict__[key]=valuea=B()
a.datadescriptor=999
print a.__dict__'''
輸出:
('B.__setattr__ ', 'datadescriptor', 999)
{'datadescriptor': 999}
'''

  當type(instance)有定義__setattr__方法時,那么是否是 descriptor就無關緊要了,都會調用這個__setattr__

  

  2,刪除instance.attr?

  刪除instance.attr和設置instacne.attr的情況非常類似,涉及到三個方法或情況:__delattr__或__delete__ , 刪除 instance.__dict__

  優先級也是和設置instance.attr一樣的:

    1.如果type(instance)定義了__delattr__,那么直接調用,無論這個attr是否為descriptor

    2.如果沒有定義__delattr__,并且是descriptor

        2.1,如果這個descriptor 定義了 __delete__,那么調用__delete__方法

        2.2如果這個descriptor 沒有定義__delete__,那么raise?AttributeError

    3.del intance.__dict__[attr]

?


三、通過class訪問屬性

通過class object來獲取attr在概念上其實和通過instance來獲取屬性是一樣的,instance 的class 是某個class object,而 class object 的class 應該是這個class的 metaclass

當在class object 的dict中找不到attr時,會轉而向 class 的metaclass的dict中去尋找.

通過ClassA.attr訪問屬性的規則為:

  1. 如果MetaClass中有__getattribute__,則直接返回該__getattribute__的結果。
  2. 如果attr是個Descriptor,則直接返回Descriptor的__get__的結果。
  3. 如果attr是class.dict中的屬性,則直接返回attr的值
  4. 如果類中沒有attr,且MetaClass中定義了__getattr__,則調用MetaClass中的__getattr__
  5. 如果類中沒有attr,且MetaClass中沒有定義__getattr__,則拋出異常AttributeError
  • ?實驗
class Metaclass(type):datadescriptor=DataDescriptor()def __new__(metaclz,name,bases,attrs):print("create new class ",metaclz,name)return type.__new__(metaclz, name, bases, attrs)def __getattr__(self,name):print("Metaclass.__getattr__ name:",name)#def __getattribute__(self,name):#	print("Metaclass.__getattribute__ name:",name)#	return name+'a'class classB(object):__metaclass__=Metaclassprint classB.datadescriptorprint classB.ss
'''
輸出('create new class ', <class '__main__.Metaclass'>, 'classB')
('DataDescriptor.__get__ ', <__main__.DataDescriptor object at 0x00BD8EF0>, <class '__main__.classB'>, <class
'__main__.Metaclass'>)
2
('Metaclass.__getattr__ name:', 'ss')
None'''

?

其實可以發現descriptor的主要作用是起到了保護作用,當某種類型的變量被訪問的時候,在給一次程序員一個控制的機會。

另外__getattr__也有類似的作用,__getattr__的用法有很多,典型的是在 web程序中,經常要有request.attr 、request[attr]這種操作,那么這個時候,把本需要用函數(類似 request.get(name) )來獲取某些狀態變量的操作,轉成?request.attr 、request[attr]這種形式,方便很多。

?

轉載于:https://www.cnblogs.com/hi0xcc/p/5586601.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/374515.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/374515.shtml
英文地址,請注明出處:http://en.pswp.cn/news/374515.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Heroku運行Java

如果您是像我這樣的Java迷&#xff0c;那么您有個好消息值得振奮。 Heroku現在運行Java&#xff01; 嗯&#xff0c;與其他流行的“ Web”語言&#xff08;如PHP / RoR&#xff09;不同&#xff0c;Java具有在Web服務器中進行部署和維護的麻煩。 一直以來&#xff0c;只有企業才…

配置了坐標還是找不到serv_為什么老人家總是這疼那疼,還找不到原因?是矯情還是另有原因...

“哎呀&#xff0c;怎么回事&#xff0c;腰痛腿痛的&#xff0c;痛的我一晚上都沒怎么睡覺&#xff0c;怎么回事昨天也沒干嘛啊&#xff01;”對于這一句話&#xff0c;相信很多朋友都不是很陌生。這句話是來自于一位網友的留言&#xff0c;而說這話的真是她的媽媽。這也是很多…

Nginx負載均衡和LVS負載均衡的比較分析

lvs和nginx都可以用作多機負載的方案&#xff0c;它們各有優缺&#xff0c;在生產環境中需要好好分析實際情況并加以利用。 首先提醒&#xff0c;做技術切不可人云亦云&#xff0c;我云即你云&#xff1b;同時也不可太趨向保守&#xff0c;過于相信舊有方式而等別人來幫你做墊被…

QuartZ.net 常用配置說明

配置文件說明app.config中的quartz部分<quartz><!-- configure Thread Pool--><addkey"quartz.threadPool.type"value"Quartz.Simpl.SimpleThreadPool,Quartz" /><addkey"quartz.threadPool.threadCount"value"10&quo…

qopenglwidget 透明_廊坊透明真空袋用途-祺泰包裝

功能方面&#xff1a;平面真空袋抽真空后易形成不平整&#xff0c;不均勻的現象。目前&#xff0c;真空包裝主要用于食品的包裝&#xff0c;如肉類、谷類加工食品以及易氧化變質的食品&#xff0c;也可用于機械零件、儀器和羽絨制品、毛制品等蓬松制品的包裝。在超shi中&#x…

使用Spring Data Neo4j進行領域建模

大家好&#xff0c;威利在這里。 上次我告訴您&#xff0c;我正在使用Neo4j和Spring Data Neo4j構建Skybase CMDB&#xff0c;我很高興收到很多對此的積極反饋。 我展示了一些代碼&#xff0c;但沒有那么多。 在本文中&#xff0c;我將向您展示如何使用Spring Data Neo4j在Skyb…

mysql 阿里內核人員

丁奇 http://dinglin.javaeye.com/鳴嵩 曹偉-鳴嵩 (新浪微博)彭立勛 http://www.penglixun.com/皓庭 http://wqtn22.iteye.com/項仲 http://blog.csdn.net/wudongxu劍川 http://gaoyusong.com/武藏 http://ybbct.iteye.com/祁奚 http://i.mtime.com/844165/褚霸 http://blog.y…

linux基本命令學習

1. 執行文件&#xff1a; ./文件 &#xff1a;執行該文件 sh startup.sh 執行該文件 2. Vi編輯 vi命令&#xff1a; :w 保存文件但不退出 :w file 將修改另外保存到file中&#xff0c;不退出 :w! 強制保存&#xff0c;不推出 :wq 保存文件并退出 :wq! 強制保存文件&#xff0…

506. 相對名次

方法一 復制數組排序返回對應下標 class Solution {public String[] findRelativeRanks(int[] score) {int n score.length;int[] newScore Arrays.copyOf(score,n);Arrays.sort(newScore);String[] res new String[n];for(int i 0;i<n;i){int rank Arrays.binarySear…

Java壓縮

在最近的項目中&#xff0c;我們不得不做一些我個人從未真正看過的事情。 壓縮。 我們需要拍幾個文件和圖像&#xff0c;將它們壓縮并提供給FTP使用&#xff0c;是的&#xff0c;總有一天&#xff0c;感覺確實回到了90年代。 除了過去的FTP之行外&#xff0c;它還是一個很好的機…

蘇州面對公司發布

假設您對我們這種創業型公司和我們的發展方向感興趣的話&#xff0c;我們希望通過以下10個問答進一步添加兩方的了解。我們希望看到的是您經過深思熟慮的、對公司和自己的前途負責任的謹慎回答。而不是應付公差式的輕描淡寫&#xff08;我們會依據您回答質量的高低決定是否邀請…

linux多線程_Java+Linux,深入內核源碼講解多線程之進程

之前寫了兩篇文章&#xff0c;都是針對Linux這個系統的&#xff0c;為什么?我為什么這么喜歡寫這個系統的知識&#xff0c;可能就是為了今天的內容多線程系列&#xff0c;現在多線程不是一個面試重點 啊&#xff0c;那如果你能深入系統內核回答這個知識點&#xff0c;面試官會…

594. 最長和諧子序列

和諧數組是指一個數組里元素的最大值和最小值之間的差別 正好是 1 。 現在&#xff0c;給你一個整數數組 nums &#xff0c;請你在所有可能的子序列中找到最長的和諧子序列的長度。 數組的子序列是一個由數組派生出來的序列&#xff0c;它可以通過刪除一些元素或不刪除元素、…

解決git clone報錯SSL certificate problem

Git新手一枚&#xff0c;今天進行git clone操作時發生如下問題&#xff1a;提示無效的鏈接error: SSL certificate problem: Invalid certificate chain while accessing https://githib.com/...XXXX.git fatal: HTTP request failed解決方法也很簡單&#xff0c;一條命令就搞定…

使用內存映射文件獲取巨大的矩陣

總覽 矩陣可能真的很大&#xff0c;有時甚至比一個數組中可以容納的更大。 您可以通過具有多個數組來擴展最大大小&#xff0c;但這會使堆大小確實很大且效率低下。 一種替代方法是在內存映射文件上使用包裝器。 內存映射文件的優點是它們對堆的影響很小&#xff0c;并且可以由…

ipad連接電腦_這些應用讓iPad生產力分分鐘UP

IT時報見習記者 錢奕昀用iPad辦公這件事&#xff0c;多年前網友就在討論&#xff0c;最常見的還是那句“買前生產力&#xff0c;買后愛奇藝”。很長一段時間里&#xff0c;它的生產力屬性都是弱于娛樂屬性的。其實&#xff0c;作為PC端和移動端的形態中和&#xff0c;iPad可以…

Mac OSX 快捷鍵命令行

ctrlshift 快速放大dock的圖標會暫時放大&#xff0c;而如果你開啟了dock放大CommandOptionW 將所有窗口關閉CommandW 將當前窗口關閉(可以關閉Safari標簽欄,很實用) CommandOptionM …

將JavaFX 2.0與Swing和SWT集成

JavaFX 2.0對JavaFX的改進之一是可以更輕松地與Swing和SWT進行互操作 。 一些在線資源記錄了如何完成此操作。 其中包括將JavaFX集成到Swing應用程序和SWT Interop中 。 但是&#xff0c;在有效的類級Javadoc文檔的一個很好的示例中&#xff0c;各自的JavaFX類javafx.embed.swi…

iOS-如何返回某個字符串的拼音助記碼

我也是看了網上的一個示例代碼后&#xff0c;在它的基礎上進行的修改。因為項目上會用到&#xff0c;我相信很多人的項目上也會用到。所以實現后&#xff0c;也趕緊分享出來&#xff0c;希望后來人不需要花費時間了。 提示&#xff1a;這里用到了正則表達式&#xff0c;使用了一…

wifi rssi 計算 距離_WiFi和WLAN是一樣的?真相在這里~別再傻傻分不清了

我們通常上網的時候會說連接WiFi如果注意到無線網絡的名稱就會發現手機的連接顯示是WLAN別再將WiFI和WLAN搞混了&#xff01;二者的定義WLANWLAN的全稱為 Wireless Local Area Networks,中文意思為無線局域網絡&#xff0c;是一種數據傳輸系統。它是利用射頻技術進行數據傳輸&a…