摘要:之所以是這樣是因?yàn)楫?dāng)訪問一個實(shí)例描述符對象時,會將轉(zhuǎn)換為。而類的字典中則有描述符對象。這主要就是因?yàn)槊枋龇麅?yōu)先。此外,非數(shù)據(jù)描述符的優(yōu)先級低于實(shí)例屬性。參考以上就是本人對描述符的一些理解,有什么不正確的地方還請不吝指出,謝謝
什么是描述符
python描述符是一個“綁定行為”的對象屬性,在描述符協(xié)議中,它可以通過方法重寫屬性的訪問。這些方法有 __get__(), __set__(), 和__delete__()。如果這些方法中的任何一個被定義在一個對象中,這個對象就是一個描述符。
描述符的調(diào)用描述符作為屬性訪問是被自動調(diào)用的。
對于類屬性描述符對象,使用type.__getattribute__,它能把Class.x轉(zhuǎn)換成Class.__dict__["x"].__get__(None, Class)。
對于實(shí)例屬性描述符對象,使用object.__getattribute__,它能把object.x轉(zhuǎn)換為type(object).__dict__["x"].__get__(object, type(object))。
下面我們具體通過實(shí)例來詳細(xì)說明描述符的使用
先定義一個描述符
class RevealAccess(object): def __init__(self, initval=None, name="var"): self.val = initval self.name = name def __get__(self, obj, objtype): print "Retrieving", self.name return self.val def __set__(self, obj, val): print "Updating", self.name self.val = val
上面實(shí)現(xiàn)了__get__和__set__。所以這是一個描述符對象。而且是一個數(shù)據(jù)描述符對象,非數(shù)據(jù)描述符對象只實(shí)現(xiàn)__get__方法。這2者之間有一些區(qū)別,下面會講到。
再定義一個調(diào)用描述符對象的類
class MyClass(object): x = RevealAccess(10, "var "x"") y = 5 print MyClass.x
訪問 MyClass.x 輸出
Retrieving var "x" 10
發(fā)現(xiàn)訪問x會去調(diào)用描述符的__get__方法。這就達(dá)到了描述符的作用,可以改變對象屬性的訪問,使用描述符的方法。因?yàn)槿绻馕銎靼l(fā)現(xiàn)x是一個描述符的話,其實(shí)在內(nèi)部是通過type.__getattribute__(),它能把MyClass.x轉(zhuǎn)換為MyClass.__dict__[“x”].__get__(None,MyClass)來訪問。
print MyClass.__dict__["x"].__get__(None, MyClass) # 輸出 Retrieving var "x" 10
描述符的對象定義為類屬性,如果定義成對象屬性會有什么不同嗎?下面我們試驗(yàn)一下
class MyClass(object): x = RevealAccess(10, "var "x"") def __init__(self): self.y = RevealAccess(11, "var "y"") print type(MyClass.x) # 輸出 """ Retrieving var "x"; """ test = MyClass() print test.y # 輸出 """ <__main__.RevealAccess object at 0x1004da410>; """
從上面的輸出,可以看到訪問類屬性的確調(diào)用了描述符的__get__方法,看到輸出的結(jié)果是int類型。而調(diào)用實(shí)例屬性并沒有訪問__get__方法。而是直接返回描述符的實(shí)例對象。之所以是這樣是因?yàn)楫?dāng)訪問一個實(shí)例描述符對象時,object.__getattribute__會將test.y轉(zhuǎn)換為type(test).__dict__[‘y’].__get__(test,type(test))。
而MyClass類中沒有“y”屬性,所以無法訪調(diào)用到_get__方法,這里會有一個判斷的過程。但這個實(shí)例對象仍然是一個描述符對象。所以最好定義描述符對象為類屬性。當(dāng)然不是不可以定義為實(shí)例屬性,請看下面
當(dāng)定義的類屬性描述符對象和實(shí)例屬性有相同的名字時
class MyClass(object): x = RevealAccess(10, "var "x"") def __init__(self, x): self.x = x
然后調(diào)用
test = MyClass(100) print test.x # 輸出 """ Updating var "x" Retrieving var "x" 100 """
可見依然調(diào)用了描述符的方法。按照常理,應(yīng)該訪問 test.__dict__["x"],然后是type(test).__dict__["x"]。由于我們定義了實(shí)例屬性x。應(yīng)該只輸出100。可這里從輸出結(jié)果看的的確確的訪問了描述符的方法。那么這是為什么呢?
其實(shí)這里主要是因?yàn)楫?dāng)python發(fā)現(xiàn)實(shí)例對象的字典中有與定義的描述符有相同名字的對象時,描述符優(yōu)先,會覆蓋掉實(shí)例屬性。python會改寫默認(rèn)的行為,去調(diào)用描述符的方法來代替。我們可以輸出類和實(shí)例對象的字典看看
test = MyClass(100) print test.__dict__ """ 輸出 {} """ print MyClass.__dict__ """ 輸出 {"__module__": "__main__", "__dict__":, "x": <__main__.RevealAccess object at 0x1004da350>, "__weakref__": , "__doc__": None, "__init__": } """
從輸出中發(fā)現(xiàn)實(shí)例對象的字典中根本就沒有x對象,即使我們在類中定義了self.x。而類的字典中則有x描述符對象。這主要就是因?yàn)槊枋龇麅?yōu)先。
上面我們定義的描述符有__get__和__set__2個方法,所以是一個數(shù)據(jù)描述符,非數(shù)據(jù)描述符只有一個__get__方法,通常用于方法。此外,非數(shù)據(jù)描述符的優(yōu)先級低于實(shí)例屬性。下面看一個例子,我們?nèi)サ鬫_set__方法。
class RevealAccess(object): def __init__(self, initval=None, name="var"): self.val = initval self.name = name def __get__(self, obj, objtype): print "Retrieving", self.name # self.val="test" return self.val # def __set__(self, obj, val): # print "Updating", self.name # self.val = val class MyClass(object): x = RevealAccess(10, "var "x"") def __init__(self, x): self.x = x test = MyClass(100) print test.x “”“ 100 “”“ print test.__dict__ “”“ {"x": 100} “”“ print MyClass.__dict__ “”“ {"__module__": "__main__", "__dict__":, "x": <;__main__.RevealAccess object at 0x1005da310>, "__weakref__": , "__doc__": None, "__init__": } “”“ print MyClass.x """ Retrieving var "x" 10 """
從上面的輸出,可以看出非數(shù)據(jù)描述符不會覆蓋掉實(shí)例屬性。而且優(yōu)先級比實(shí)例屬性低。這也是和數(shù)據(jù)描述符的一個區(qū)別。
綜上所述,對于描述符的調(diào)用有以下幾點(diǎn)需要注意
描述符被 getattribute 方法調(diào)用
覆蓋__getattribute__會讓描述符無法自動調(diào)用
描述符只適用于新式類,即繼承object的類
object . getattribute 和 type . getattribute 調(diào)用__get__方法不一樣
數(shù)據(jù)描述符優(yōu)先于實(shí)例的字典,對于相同名字的會覆蓋
實(shí)例的字典優(yōu)先于非數(shù)據(jù)描述符。但不會覆蓋。
對于數(shù)據(jù)描述符,python中property就是一個典型的應(yīng)用。
對于非數(shù)據(jù)描述符,其主要用于方法。如靜態(tài)方法和類方法。看源碼可以看到只實(shí)現(xiàn)了描述符協(xié)議中的__get__方法,而沒有實(shí)現(xiàn)__set__和__del__。
如下面這樣模擬靜態(tài)方法
class StaticMethod(object): def __init__(self, f): self.f = f def __get__(self, obj, objtype=None): return self.f class MyClass(object): @StaticMethod def get_x(x): print("static") return x print MyClass.get_x(100) """ static 100 “”“
調(diào)用MyClass.get_x(100)相當(dāng)于
MyClass.__dict__["get_x"].__get__(None, MyClass)(100)
我們知道在python中,一切皆是對象。每一個定義的方法其實(shí)都是一個對象。在這里我們可以通過dir()查看每一個方法里的屬性和方法。看下面
class Desc(object): def test1(self): print("test1") def test2(): print("test2") print(dir(test2)) """輸出太長不貼了,但從輸出中可以看到有__get__""" print(dir(Desc.test1)) """ ["__call__", "__class__", "__cmp__", "__delattr__", "__doc__", "__format__", "__func__", "__get__", "__getattribute__", "__hash__", "__init__", "__new__", "__reduce__", "__reduce_ex__", "__repr__", "__self__", "__setattr__", "__sizeof__", "__str__", "__subclasshook__", "im_class", "im_func", "im_self"] """
從dir的輸出,可以看到,每個方法對象都包含一個__get__方法。因此可以說每一個方法都是一個非數(shù)據(jù)描述符。通常我們通過點(diǎn)操作符調(diào)用方法時,內(nèi)部都是調(diào)用這個__get__方法。
參考 https://docs.python.org/2.7/h...
以上就是本人對描述符的一些理解,有什么不正確的地方還請不吝指出,謝謝!
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/38242.html
摘要:最近在閱讀微型框架的源碼,發(fā)現(xiàn)了中有一個既是裝飾器類又是描述符的有趣實(shí)現(xiàn)。所以第三版的代碼可以這樣寫第三版的代碼沒有使用裝飾器,而是使用了描述符這個技巧。更大的問題來自如何將描述符與裝飾器結(jié)合起來,因?yàn)槭且粋€類而不是方法。 最近在閱讀Python微型Web框架Bottle的源碼,發(fā)現(xiàn)了Bottle中有一個既是裝飾器類又是描述符的有趣實(shí)現(xiàn)。剛好這兩個點(diǎn)是Python比較的難理解,又混合在...
摘要:近日,他開通了賬號,并發(fā)表了第一篇文章,透露出要替換的核心部件解析器的想法。這篇文章分析了當(dāng)前的解析器的諸多缺陷,并介紹了解析器的優(yōu)點(diǎn),令人振奮。但問題是,如果你這樣寫語法,解析器不會起作用,將會罷工。 showImg(https://segmentfault.com/img/remote/1460000019893712?w=3936&h=2624); 花下貓語: Guido van...
摘要:下面我們用描述符來實(shí)現(xiàn)中的動態(tài)屬性和特性中提及的訂單結(jié)算代碼第四版使用描述符實(shí)現(xiàn)訂單結(jié)算功能描述符基于協(xié)議實(shí)現(xiàn),無需創(chuàng)建子類。特性是覆蓋型描述符。非覆蓋型描述符沒有實(shí)現(xiàn)方法的描述符屬于非覆蓋型描述符。類中定義的方法是非覆蓋型描述符。 導(dǎo)語:本文章記錄了本人在學(xué)習(xí)Python基礎(chǔ)之元編程篇的重點(diǎn)知識及個人心得,打算入門Python的朋友們可以來一起學(xué)習(xí)并交流。 本文重點(diǎn): 1、了解描述符...
摘要:由上面的注釋,可以看出其實(shí)就相當(dāng)于一個描述符類,而在此刻變成了一個描述符。調(diào)用這個方法可以知道,每調(diào)用一次,它都會經(jīng)過描述符類的。基于描述符如何實(shí)現(xiàn)同樣的也是一樣。我想你應(yīng)該對描述符在中的應(yīng)用有了更深的理解。好吧,我承認(rèn)我標(biāo)題黨了。但是這篇文章的知識點(diǎn),你有極大的可能并不知道。 前段時間,我寫了一篇描述符的入門級文章,從那些文章里你知道了如何定義描述符,且明白了描述符是如何工作的。 如果你還...
閱讀 2008·2021-11-24 09:39
閱讀 1143·2021-09-10 11:25
閱讀 1769·2021-09-08 10:42
閱讀 3733·2021-09-06 15:00
閱讀 2498·2019-08-30 15:54
閱讀 3116·2019-08-29 17:08
閱讀 3272·2019-08-29 11:26
閱讀 2840·2019-08-28 18:27