国产睡熟迷奷白丝护士系列精品,中文色字幕网站,免费h网站在线观看的,亚洲开心激情在线

      <sup id="hb9fh"></sup>
          1. 千鋒教育-做有情懷、有良心、有品質的職業(yè)教育機構

            手機站
            千鋒教育

            千鋒學習站 | 隨時隨地免費學

            千鋒教育

            掃一掃進入千鋒手機站

            領取全套視頻
            千鋒教育

            關注千鋒學習站小程序
            隨時隨地免費學習課程

            當前位置:首頁  >  技術干貨  > Python之描述符

            Python之描述符

            來源:千鋒教育
            發(fā)布人:xqq
            時間: 2023-11-06 22:26:43 1699280803

            Descriptors(描述符)是Python語言中一個深奧但很重要的一個黑魔法,它被廣泛應用于Python語言的內核,熟練掌握描述符將會為Python程序員的工具箱添加一個額外的技巧。本文我將講述描述符的定義以及一些常見的場景,并且在文末會補充一下__getattr,__getattribute__,__getitem__這三個同樣涉及到屬性訪問的魔術方法。

            描述符的定義

            descr__get__(self,obj,objtype=None)-->value

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

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

            只要一個objectattribute(對象屬性)定義了上面三個方法中的任意一個,那么這個類就可以被稱為描述符類。

            描述符基礎

            下面這個例子中我們創(chuàng)建了一個RevealAcess類,并且實現(xiàn)了__get__方法,現(xiàn)在這個類可以被稱為一個描述符類。

            classRevealAccess(object):

            def__get__(self,obj,objtype):

            print('selfinRevealAccess:{}'.format(self))

            print('self:{}\nobj:{}\nobjtype:{}'.format(self,obj,objtype))

            classMyClass(object):

            x=RevealAccess()

            deftest(self):

            print('selfinMyClass:{}'.format(self))

            EX1實例屬性

            接下來我們來看一下__get__方法的各個參數(shù)的含義,在下面這個例子中,self即RevealAccess類的實例x,obj即MyClass類的實例m,objtype顧名思義就是MyClass類自身。從輸出語句可以看出,m.x訪問描述符x會調用__get__方法。

            >>>m=MyClass()

            >>>m.test()

            selfinMyClass:<__main__.MyClassobjectat0x7f19d4e42160>

            >>>m.x

            selfinRevealAccess:<__main__.RevealAccessobjectat0x7f19d4e420f0>

            self:<__main__.RevealAccessobjectat0x7f19d4e420f0>

            obj:<__main__.MyClassobjectat0x7f19d4e42160>

            objtype:

            EX2類屬性

            如果通過類直接訪問屬性x,那么obj接直接為None,這還是比較好理解,因為不存在MyClass的實例。

            >>>MyClass.x

            selfinRevealAccess:<__main__.RevealAccessobjectat0x7f53651070f0>

            self:<__main__.RevealAccessobjectat0x7f53651070f0>

            obj:None

            objtype:

            描述符的原理

            描述符觸發(fā)

            上面這個例子中,我們分別從實例屬性和類屬性的角度列舉了描述符的用法,下面我們來仔細分析一下內部的原理:

            如果是對實例屬性進行訪問,實際上調用了基類object的__getattribute__方法,在這個方法中將obj.d轉譯成了type(obj).__dict__['d'].__get__(obj,type(obj))。

            如果是對類屬性進行訪問,相當于調用了元類type的__getattribute__方法,它將cls.d轉譯成cls.__dict__['d'].__get__(None,cls),這里__get__()的obj為的None,因為不存在實例。

            簡單講一下__getattribute__魔術方法,這個方法在我們訪問一個對象的屬性的時候會被無條件調用,詳細的細節(jié)比如和__getattr,__getitem__的區(qū)別我會在文章的末尾做一個額外的補充,我們暫時并不深究。

            描述符優(yōu)先級

            首先,描述符分為兩種:

            如果一個對象同時定義了__get__()和__set__()方法,則這個描述符被稱為datadescriptor。

            如果一個對象只定義了__get__()方法,則這個描述符被稱為non-datadescriptor。

            我們對屬性進行訪問的時候存在下面四種情況:

            datadescriptor

            instancedict

            non-datadescriptor

            __getattr__()

            它們的優(yōu)先級大小是:

            datadescriptor>instancedict>non-datadescriptor>__getattr__()

            這是什么意思呢?就是說如果實例對象obj中出現(xiàn)了同名的datadescriptor->d和instanceattribute->d,obj.d對屬性d進行訪問的時候,由于datadescriptor具有更高的優(yōu)先級,Python便會調用type(obj).__dict__['d'].__get__(obj,type(obj))而不是調用obj.__dict__[‘d’]。但是如果描述符是個non-datadescriptor,Python則會調用obj.__dict__['d']。

            Property

            每次使用描述符的時候都定義一個描述符類,這樣看起來非常繁瑣。Python提供了一種簡潔的方式用來向屬性添加數(shù)據(jù)描述符。

            property(fget=None,fset=None,fdel=None,doc=None)->propertyattribute

            fget、fset和fdel分別是類的getter、setter和deleter方法。我們通過下面的一個示例來說明如何使用Property:

            classAccount(object):

            def__init__(self):

            self._acct_num=None

            defget_acct_num(self):

            returnself._acct_num

            defset_acct_num(self,value):

            self._acct_num=value

            defdel_acct_num(self):

            delself._acct_num

            acct_num=property(get_acct_num,set_acct_num,del_acct_num,'_acct_numproperty.')

            如果acct是Account的一個實例,acct.acct_num將會調用getter,acct.acct_num=value將調用setter,delacct_num.acct_num將調用deleter。

            >>>acct=Account()

            >>>acct.acct_num=1000

            >>>acct.acct_num

            1000

            Python也提供了@property裝飾器,對于簡單的應用場景可以使用它來創(chuàng)建屬性。一個屬性對象擁有getter,setter和deleter裝飾器方法,可以使用它們通過對應的被裝飾函數(shù)的accessor函數(shù)創(chuàng)建屬性的拷貝。

            classAccount(object):

            def__init__(self):

            self._acct_num=None

            @property

            #the_acct_numproperty.thedecoratorcreatesaread-onlyproperty

            defacct_num(self):

            returnself._acct_num

            @acct_num.setter

            #the_acct_numpropertysettermakesthepropertywriteable

            defset_acct_num(self,value):

            self._acct_num=value

            @acct_num.deleter

            defdel_acct_num(self):

            delself._acct_num

            如果想讓屬性只讀,只需要去掉setter方法。

            在運行時創(chuàng)建描述符

            我們可以在運行時添加property屬性:

            classPerson(object):

            defaddProperty(self,attribute):

            #createlocalsetterandgetterwithaparticularattributename

            getter=lambdaself:self._getProperty(attribute)

            setter=lambdaself,value:self._setProperty(attribute,value)

            #constructpropertyattributeandaddittotheclass

            setattr(self.__class__,attribute,property(fget=getter,\

            fset=setter,\

            doc="Auto-generatedmethod"))

            def_setProperty(self,attribute,value):

            print("Setting:{}={}".format(attribute,value))

            setattr(self,'_'+attribute,value.title())

            def_getProperty(self,attribute):

            print("Getting:{}".format(attribute))

            returngetattr(self,'_'+attribute)

            >>>user=Person()

            >>>user.addProperty('name')

            >>>user.addProperty('phone')

            >>>user.name='johnsmith'

            Setting:name=johnsmith

            >>>user.phone='12345'

            Setting:phone=12345

            >>>user.name

            Getting:name

            'JohnSmith'

            >>>user.__dict__

            {'_phone':'12345','_name':'JohnSmith'}

            靜態(tài)方法和類方法

            我們可以使用描述符來模擬Python中的@staticmethod和@classmethod的實現(xiàn)。我們首先來瀏覽一下下面這張表:

            靜態(tài)方法

            對于靜態(tài)方法f。c.f和C.f是等價的,都是直接查詢object.__getattribute__(c,‘f’)或者object.__getattribute__(C,’f‘)。靜態(tài)方法一個明顯的特征就是沒有self變量。

            靜態(tài)方法有什么用呢?假設有一個處理專門數(shù)據(jù)的容器類,它提供了一些方法來求平均數(shù),中位數(shù)等統(tǒng)計數(shù)據(jù)方式,這些方法都是要依賴于相應的數(shù)據(jù)的。但是類中可能還有一些方法,并不依賴這些數(shù)據(jù),這個時候我們可以將這些方法聲明為靜態(tài)方法,同時這也可以提高代碼的可讀性。

            使用非數(shù)據(jù)描述符來模擬一下靜態(tài)方法的實現(xiàn):

            classStaticMethod(object):

            def__init__(self,f):

            self.f=f

            def__get__(self,obj,objtype=None):

            returnself.f

            我們來應用一下:

            classMyClass(object):

            @StaticMethod

            defget_x(x):

            returnx

            print(MyClass.get_x(100))#output:100

            類方法

            Python的@classmethod和@staticmethod的用法有些類似,但是還是有些不同,當某些方法只需要得到類的引用而不關心類中的相應的數(shù)據(jù)的時候就需要使用classmethod了。

            使用非數(shù)據(jù)描述符來模擬一下類方法的實現(xiàn):

            classClassMethod(object):

            def__init__(self,f):

            self.f=f

            def__get__(self,obj,klass=None):

            ifklassisNone:

            klass=type(obj)

            defnewfunc(*args):

            returnself.f(klass,*args)

            returnnewfunc

            其他的魔術方法

            首次接觸Python魔術方法的時候,我也被__get__,__getattribute__,__getattr__,__getitem__之間的區(qū)別困擾到了,它們都是和屬性訪問相關的魔術方法,其中重寫__getattr__,__getitem__來構造一個自己的集合類非常的常用,下面我們就通過一些例子來看一下它們的應用。

            __getattr__

            Python默認訪問類/實例的某個屬性都是通過__getattribute__來調用的,__getattribute__會被無條件調用,沒有找到的話就會調用__getattr__。如果我們要定制某個類,通常情況下我們不應該重寫__getattribute__,而是應該重寫__getattr__,很少看見重寫__getattribute__的情況。

            從下面的輸出可以看出,當一個屬性通過__getattribute__無法找到的時候會調用__getattr__。

            In[1]:classTest(object):

            ...:def__getattribute__(self,item):

            ...:print('call__getattribute__')

            ...:returnsuper(Test,self).__getattribute__(item)

            ...:def__getattr__(self,item):

            ...:return'call__getattr__'

            ...:

            In[2]:Test().a

            call__getattribute__

            Out[2]:'call__getattr__'

            應用

            對于默認的字典,Python只支持以obj['foo']形式來訪問,不支持obj.foo的形式,我們可以通過重寫__getattr__讓字典也支持obj['foo']的訪問形式,這是一個非常經(jīng)典常用的用法:

            classStorage(dict):

            """AStorageobjectislikeadictionaryexceptobj.foocanbeusedinadditiontoobj['foo']."""

            def__getattr__(self,key):

            try:

            returnself[key]

            exceptKeyErrorask:

            raiseAttributeError(k)

            def__setattr__(self,key,value):

            self[key]=value

            def__delattr__(self,key):

            try:

            delself[key]

            exceptKeyErrorask:

            raiseAttributeError(k)

            def__repr__(self):

            return''!

            我們來使用一下我們自定義的加強版字典:

            >>>s=Storage(a=1)

            >>>s['a']

            1

            >>>s.a

            1

            >>>s.a=2

            >>>s['a']

            2

            >>>dels.a

            >>>s.a

            ...

            AttributeError:'a'

            __getitem__

            getitem用于通過下標[]的形式來獲取對象中的元素,下面我們通過重寫__getitem__來實現(xiàn)一個自己的list。

            classMyList(object):

            def__init__(self,*args):

            self.numbers=args

            def__getitem__(self,item):

            returnself.numbers[item]

            my_list=MyList(1,2,3,4,6,5,3)

            printmy_list[2]

            這個實現(xiàn)非常的簡陋,不支持slice和step等功能,請讀者自行改進,這里我就不重復了。

            應用

            下面是參考requests庫中對于__getitem__的一個使用,我們定制了一個忽略屬性大小寫的字典類。

            程序有些復雜,我稍微解釋一下:由于這里比較簡單,沒有使用描述符的需求,所以使用了@property裝飾器來代替,lower_keys的功能是將實例字典中的鍵全部轉換成小寫并且存儲在字典self._lower_keys中。重寫了__getitem__方法,以后我們訪問某個屬性首先會將鍵轉換為小寫的方式,然后并不會直接訪問實例字典,而是會訪問字典self._lower_keys去查找。賦值/刪除操作的時候由于實例字典會進行變更,為了保持self._lower_keys和實例字典同步,首先清除self._lower_keys的內容,以后我們重新查找鍵的時候再調用__getitem__的時候會重新新建一個self._lower_keys。

            classCaseInsensitiveDict(dict):

            @property

            deflower_keys(self):

            ifnothasattr(self,'_lower_keys')ornotself._lower_keys:

            self._lower_keys=dict((k.lower(),k)forkinself.keys())

            returnself._lower_keys

            def_clear_lower_keys(self):

            ifhasattr(self,'_lower_keys'):

            self._lower_keys.clear()

            def__contains__(self,key):

            returnkey.lower()inself.lower_keys

            def__getitem__(self,key):

            ifkeyinself:

            returndict.__getitem__(self,self.lower_keys[key.lower()])

            def__setitem__(self,key,value):

            dict.__setitem__(self,key,value)

            self._clear_lower_keys()

            def__delitem__(self,key):

            dict.__delitem__(self,key)

            self._lower_keys.clear()

            defget(self,key,default=None):

            ifkeyinself:

            returnself[key]

            else:

            returndefault

            我們來調用一下這個類:

            >>>d=CaseInsensitiveDict()

            >>>d['ziwenxie']='ziwenxie'

            >>>d['ZiWenXie']='ZiWenXie'

            >>>print(d)

            {'ZiWenXie':'ziwenxie','ziwenxie':'ziwenxie'}

            >>>print(d['ziwenxie'])

            ziwenxie

            #d['ZiWenXie']=>d['ziwenxie']

            >>>print(d['ZiWenXie'])

            ziwenxie

            以上內容為大家介紹了Python之描述符,希望對大家有所幫助,如果想要了解更多Python相關知識,請關注多測師。http://www.parentadvocate.org/xwzx/

            tags: python培訓
            聲明:本站稿件版權均屬千鋒教育所有,未經(jīng)許可不得擅自轉載。
            10年以上業(yè)內強師集結,手把手帶你蛻變精英
            請您保持通訊暢通,專屬學習老師24小時內將與您1V1溝通
            免費領取
            今日已有369人領取成功
            劉同學 138****2860 剛剛成功領取
            王同學 131****2015 剛剛成功領取
            張同學 133****4652 剛剛成功領取
            李同學 135****8607 剛剛成功領取
            楊同學 132****5667 剛剛成功領取
            岳同學 134****6652 剛剛成功領取
            梁同學 157****2950 剛剛成功領取
            劉同學 189****1015 剛剛成功領取
            張同學 155****4678 剛剛成功領取
            鄒同學 139****2907 剛剛成功領取
            董同學 138****2867 剛剛成功領取
            周同學 136****3602 剛剛成功領取
            相關推薦HOT