日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

python中的新式类与旧式类的一些基于descriptor的概念(下)

發布時間:2023/11/28 生活经验 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python中的新式类与旧式类的一些基于descriptor的概念(下) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
      • 3. Descriptor介紹
        • 3.1 Descriptor代碼示例
        • 3.2 定義
        • 3.3 Descriptor Protocol(協議)
        • 3.4 Descriptor調用方法
      • 4. 基于Descriptor實現的功能
        • 4.1 property
        • 4.2 函數和方法,綁定與非綁定
        • 4.3 super
      • 5. 結尾

3. Descriptor介紹

3.1 Descriptor代碼示例

class RevealAccess(object):
????"""創建一個Descriptor類,用來打印出訪問它的操作信息
????"
""

????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
#使用Descriptor
?class MyClass(object):
?????? #生成一個Descriptor實例,賦值給類MyClass的x屬性
???????x = RevealAccess(10, 'var "x"')
???????y = 5 #普通類屬性運行結果:

3.2 定義

descriptor可以說是一個綁定了特定訪問方法的類屬性,這些訪問方法是重寫了descriptor protocol中的三個方法,分別是__get__, __set__, __del__方法。如果三個中任一一個方法在 對象中定義了,就說這個對象是一個descriptor對象,可以把這個對象賦值給其它屬性。descriptor protocol 可以看成是一個有三個方法的接口。 ? 通常對一個實例的屬性的訪問操作,如get, set, delete是通過實例的__dict__字典屬性進行的, 例如,對于操作a.x,會一個查找鏈從a.__dict['x'](實例的字典),再到type(a).__dict__['x'](類的 字典),再到type(a)的父類的字典等等。代碼如下: ? 可以看出類和實例的字典屬性的值的內容其它是不一樣的,因為實例中有綁定屬性的存在。type(a) 返回就是實例a的類型,類A。 ? 如果這個需要被查找的屬性是一個定義了descriptor協議方法的對象,那么python就不會按照默認的 查找方式,而是調用descriptor協議中定義的方法去做處理。descriptor只對新式類和新式實例有效。

3.3 Descriptor Protocol(協議)

有下面這三個方法 get__(self, obj, type=None) --> valueset__(self, obj, value) --> Nonedelete__(self, obj) --> None?只要對象重寫任何上面的一個方法,對象就被看作是descriptor,就可以不去采用默認的查找屬性的順序。??如果一個對象同時定義了__get__,__set__方法,被看作是data descriptor;只定義了__get__,被稱為non-data descriptor。如果實例字典中有一個key和data descriptor同名,那么查找時優先采用data descriptor;如果實例字典中有一個key和non-data descriptor同名,那么優先采用實例字典的方法。??創建一個只讀data descriptor,只需要在同時定義__get__,__set__方法的同時,讓__set__方法拋出異常AttributeError。

3.4 Descriptor調用方法

可以直接使用descriptor實例進行方法調用,如d.__get__(obj),但我這樣嘗試會報錯。。。 ? 一般是在屬性訪問的時候自動被調用,例如obj.d是在obj實例的字典屬性中查找d變量,如果d定義了__get__ 方法和__set__方法,是一個data descriptor,則根據上面提到的優先級,會自動去調用 d.__get__(obj)。 ? 對于實例來說,對于任意的屬性訪問實現的內部機制是使用object.__getatrribute__, 把b.x轉化為type(b).__dict__['x'].__get__(b, type(b)),實現的優先級是按data descriptor > instance variables > non-data descriptor > __getattr__(如果定義了的話)。 ? 對于類來說,是使用type.__getattribute__,把B.x轉化為B.__dict__['x'].__get__(None, B)。 ? 用純python語言來描述的類屬性的訪問的話,大概是這個樣子: def __getattribute__(self, key):
????"模擬type.__getattribute__()實現"
????#通過默認方式查找到目標
????v = object.__getattribute__(self, key)
????#如果目標屬性含有__get__方法,則表示它是一個descriptor
????if hasattr(v, '__get__'):
???????#優先使用descriptor中定義的方法返回值
???????return v.__get__(None, self)
????#如果不是descriptor,就按默認的方式返回
????return v ? 需要注意的幾點:
  • descriptor是被__getattribute__方法調用的。
  • 重寫__getattribute__方法,會阻止自動的descriptor調用,必要時需要你自己加上去。
  • __getattribute__方法只在新式類和新式實例中有用。
  • object.__getattribute__和class.__getattribute__會用不一樣的方式調用__get__
  • data descriptors總是覆蓋instance dictionary
  • non-data descriptors有可能被instance dictionary覆蓋
使用super()返回的對象也有一個__getattribute__方法來調用descriptor。對于super(B, obj).m() 是在obj.__class__.__mro__(類屬性__mro__)查找路徑中找類B的基類A,然后再調用A.__dict__['m'].__get__(obj, A)。 如果m不是descriptor,則直接返回;如果不在A的字典里,則會使用object.__getattribute__進行查找。 ? 可以看到,descriptor實現的細節被定義在了object, type和super()的__getattribute__方法中。新式類從object繼承了 這一特性,或者也可以通過元類的實現去完成類似的用法,同樣的,類定義時也可以通過重寫__getattribute__方法來關閉 descirptor的調用。

4. 基于Descriptor實現的功能

descriptor協議是簡單而又強大的,新式類中的一些新特性就是利用descriptor功能封裝成一個獨立的函數調用,如:
  • Property
  • 綁定和非綁定方法
  • 靜態方法
  • 類方法
  • super

4.1 property

調用proprety()是一種創建data descriptor的一種簡潔的方式,函數結構如下: property(fget=None, fset=None, fdel=None, doc=None) #返回的是property對象,可以賦值給某屬性, propety方法有四個參數,只要對沒有進行賦值的參數進行訪問就會報錯。 ? x?是 C 的一個實例, attrib是C中定義的一個property屬性:當你引用 x.attrib 時, python調用 fget 方法取值給你. 當你為x.attrib賦值: x.attrib=value 時, python調用 fset方法, 并且value值做為fset方法的參數, 當你執行del x.attrib 時, python調用fdel方法, 當你傳過去的名為 doc 的參數即為該屬性的文檔字符串. ? 用法如下: class C(object):
????def getX(self):
????????print 'get x'
????????return self.__x
????def setX(self, value):
????????print 'set x', value
????????self.__x = value
????def delX(self):
????????print 'del x'
????????del self.__x
????x = property(getX, setX, delX, "This is 'x' property.") 運行結果如下: ? 非常方便地就改變了默認的訪問屬性x的方式。又如,我們定義一個只讀property屬性: class Rect(object):
????def __init__(self, width, heigth):
????????self.width = width
????????self.heigth = heigth
????def getArea(self):
????????return self.width * self.heigth
????area = property(getArea, doc='area of the rectangle') 只需要傳入fget參數就可以,運行如下: ?? 屬性area為只讀,任何重新綁定和刪除的操作都會報錯。這是因為我們只定義了fget方法。 ? properties所做的事情與那些特殊方法__getattr__, __setattr__, __delattr__ 等是極其相似的, 不過同樣的工作它干起來更簡單更快捷.? 區別在于:在經典類中,當你想要改變屬性的訪問方式時,只能重載__getattr__,__setattr__方法,不過這樣會對所有的屬性訪問方式進行改動;而使用property方法就可以在不影響其它屬性的前提下,任意地對某個屬性的訪問方式進行改動,這樣做更加靈活。 ? 如果要用python語言來描述property功能實現的話,可以把property對象定義為這樣一個descritptor是: class Property(object):
????"模擬在Objects/descrobject.c文件中的PyProperty_Type()函數"

????def __init__(self, fget=None, fset=None, fdel=None, doc=None):
????????self.fget = fget
????????self.fset = fset
????????self.fdel = fdel
????????self.__doc__ = doc

????def __get__(self, obj, objtype=None):
????????if obj is None:
????????????return self?????????
????????if self.fget is None:
????????????raise AttributeError, "unreadable attribute"
????????return self.fget(obj)

????def __set__(self, obj, value):
????????if self.fset is None:
????????????raise AttributeError, "can't set attribute"
????????self.fset(obj, value)

????def __delete__(self, obj):
????????if self.fdel is None:
????????????raise AttributeError, "can't delete attribute"
????????self.fdel(obj) ? 關于property()方法的幾點聲明: 1. 它不適用于經典類,但你在經典類中使用的時候,也不會報錯,表面上好像OK,但實際上是不會調用 參數中你設置的訪問函數的。比如,當你設置一個新的屬性時,經典類只是傳統的在__dict__上加上了它, 而不去調用fset函數進行設置。也許你可以在__setattr__函數中修復這一問題,但代價太高。 ? 2. property()函數的四個參數,應該為methods(帶self參數的那種),而不是function. ? 3. 當你使用類去訪問屬性的時候,property設置的函數是不會被調用的。只有用實例去訪問才會調用。

4.2 函數和方法,綁定與非綁定

Python的面向對象特性是基于函數的,函數的實現是需要使用到non-data descriptor的功能。?類字典把方法存放為函數。在類定義中,方法是由def或者lambda聲明的。和一般函數不同的是,方法的第一個參數是self對象。?為了支持方法的調用,在訪問方法屬性的時候,functions使用相應的__get__方法。這就意味著所有的函數都是non-datadescriptor,用于根據類或者對象的調用來返回unbound或者bound的方法。用python語言可以這樣描述:class Function(object):
????. . .
????def __get__(self, obj, objtype=None):
????????"模擬Objects/funcobject.c文件中的func_descr_get()"
????????return types.MethodType(self, obj, objtype)可以在解釋器中運行一下:可以看到方法在字典中存放的類型其實是函數對象,bound和unbound方法是兩個不同的類型。內部的實現其實是一個同一個對象,不同的是這個對象的im_self屬性是否被賦值,或者是設為None。

4.3 super

在支持多繼承的語言中,討論誰是父類,感覺意義不大,尤其是像之類mro的菱形問題,父類是誰就更說不清了。需要強調的是super不會返回父類,它返回的是代理對象。理對象就是利用委托(delegation)使用別的對象的方法來實現功能的對象。?super返回的是一個定制了__getattribute__方法的對象,是一個代理對象,它可以訪問MRO中的方法。形式如下:super(cls, instance-or-subclass).method(*args, **kw)可以轉化為:right-method-in-the-MRO-applied-to(instance-or-subclass, *args, **kw)需要注意的是,第二個參數instnce-or-subclass可以是第一個參數的實例。如果返回了非綁定的方法,調用的時候需要加上第一個self參數。?通過descriptor的實現,可以說super也是一個non-data descriptor類。也就是實現了__get__(self, obj, objtyp=None)的類。假設descr是C類的一個descriptor,C.descr實現上調用的是descr.__get__(None, C);如果是實例來調用,c.descr調用的是descr.__get__(c, type(c))。?super功能用python語言來描述的話,可以是這樣:class Super(object):
????def __init__(self, type, obj=None):
????????self.__type__ = type
????????self.__obj__ = obj
????def __get__(self, obj, type=None):
????????if self.__obj__ is None and obj is not None:
????????????return Super(self.__type__, obj)
????????else:
????????????return self
????def __getattr__(self, attr):
????????if isinstance(self.__obj__, self.__type__):
????????????starttype = self.__obj__.__class__
????????else:
????????????starttype = self.__obj__
????????mro = iter(starttype.__mro__)
????????for cls in mro:
????????????if cls is self.__type__:
????????????????break
????????# Note: mro is an iterator, so the second loop
????????# picks up where the first one left off!
????????for cls in mro:
????????????if attr in cls.__dict__:
????????????????x = cls.__dict__[attr]
????????????????if hasattr(x, "__get__"):
????????????????????x = x.__get__(self.__obj__)
????????????????return x
????????raise AttributeError, attr

5. 結尾

在這里,就介紹完了基于descriptor的新式類的新特性。歡迎大家討論。

轉載自:

作者:btchenguang 出處:http://www.cnblogs.com/btchenguang/


總結

以上是生活随笔為你收集整理的python中的新式类与旧式类的一些基于descriptor的概念(下)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。