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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

描述符

發(fā)布時(shí)間:2023/12/2 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 描述符 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
描述符

描述符也是面向進(jìn)階的一種,由于它的涉及比較廣,所以單獨(dú)講。

一、描述符

描述符本質(zhì)就是一個(gè)新式類,在這個(gè)新式類中,至少實(shí)現(xiàn)了__get__(),__set__(),__delete__()中的一個(gè),這也被稱為描述符協(xié)議。

描述符的作用是用來代理另外一個(gè)類的屬性,必須把描述符定義成一個(gè)類的類屬性,不能定義到構(gòu)造函數(shù)中。

描述符分為兩種,一種是數(shù)據(jù)描述符:至少實(shí)現(xiàn)了__get__()和__set__();另一種是非數(shù)據(jù)描述符:沒有實(shí)現(xiàn)__set__()。

1.描述符格式

class Foo: #在python3中任何類都是新式類,它只要實(shí)現(xiàn)了三種方法之一,那么這個(gè)類就被稱作一個(gè)描述符def __get__(self, instance, owner): #調(diào)用一個(gè)屬性時(shí)觸發(fā)passdef __set__(self, instance, value): #為一個(gè)屬性賦值時(shí)觸發(fā)passdef __delete__(self, instance): #采用del刪除屬性時(shí)觸發(fā)pass

2.描述符的使用

描述符該如何使用呢?具體代碼如下:

class Foo: #描述符def __get__(self, instance, owner):print('我是get方法')def __set__(self, instance, value):print('我是set方法')def __delete__(self, instance):print('我是delete方法') class Bar:x = Foo() #把描述符代理一個(gè)類的屬性def __init__(self,n): self.x = nb1 = Bar(10) b1.x = 11111 b1.x del b1.x

執(zhí)行代碼結(jié)果為:

我是set方法 我是set方法 我是get方法 我是delete方法

可以看出在實(shí)例化時(shí)也會(huì)觸發(fā)__set__方法,因?yàn)樵陬怋ar的初始化函數(shù)中self.x是被描述符代理的屬性。那么對(duì)象b1中的屬性字典中到底存不存在x這個(gè)值呢?具體代碼如下所示:

class Foo(): #描述符def __get__(self, instance, owner):print('我是get方法')def __set__(self, instance, value):print('我是set方法')def __delete__(self, instance):print('我是delete方法') class Bar:x = Foo()def __init__(self,n): self.x = nb1 = Bar(10) print(b1.__dict__) b1.x = 11111 print(b1.__dict__)

執(zhí)行結(jié)果為:

我是set方法 {} 我是set方法 {}

這是什么情況?無論是實(shí)例化操作還是賦值操作,在對(duì)象b1的屬性字典中仍然為空。正是因?yàn)槊枋龇年P(guān)系,它相當(dāng)于把被描述符的類的調(diào)用屬性操作、賦值操作、刪除操作都賦予另一個(gè)類來實(shí)現(xiàn),跟本身類并沒有關(guān)系,當(dāng)然這關(guān)系到優(yōu)先級(jí)的問題。

3.描述符的優(yōu)先級(jí)

我們要嚴(yán)格遵守優(yōu)先級(jí):類屬性>數(shù)據(jù)描述符>實(shí)例屬性>非數(shù)據(jù)描述符>找不到的屬性觸發(fā)__getattr__()。

(1)我們先對(duì)類屬性>數(shù)據(jù)描述符進(jìn)行分析,具體代碼如下:

class Foo():def __get__(self, instance, owner):print('我是get方法')def __set__(self, instance, value):print('我是set方法')def __delete__(self, instance):print('我是delete方法') class Bar:x = Foo()Bar.x=111 print(Bar.x) #結(jié)果為:111

上述代碼打印結(jié)果仍為111,并沒有執(zhí)行數(shù)據(jù)描述符,是因?yàn)锽ar類調(diào)用了本來要被描述符代理的屬性x進(jìn)行了修改。所有類屬性的優(yōu)先級(jí)比數(shù)據(jù)描述符高。

(2)數(shù)據(jù)描述符>實(shí)例屬性的分析代碼如下:

class Foo():def __get__(self, instance, owner):print('我是get方法')def __set__(self, instance, value):print('我是set方法')def __delete__(self, instance):print('我是delete方法') class Bar:x = Foo()b1 = Bar() b1.x #結(jié)果為:我是get方法 b1.x = 111 #結(jié)果為:我是set方法

上述代碼Foo類被定義成Bar類的類屬性,即對(duì)Bar類進(jìn)行實(shí)例化,實(shí)例屬性只執(zhí)行數(shù)據(jù)描述符的方法。說明了數(shù)據(jù)描述符的優(yōu)先級(jí)高于實(shí)例屬性。

(3)實(shí)例屬性>非數(shù)據(jù)描述符的分析代碼如下:

class Foo():def __get__(self, instance, owner):print('我是get方法') class Bar:x = Foo()b1 = Bar() b1.x = 111 print(b1.__dict__) #結(jié)果為:{'x': 111}

上述代碼可以看出實(shí)例給自己屬性進(jìn)行賦值操作,可以在實(shí)例的屬性字典中找到,這說明了實(shí)例屬性的優(yōu)先級(jí)高于非數(shù)據(jù)描述符。

(4)非數(shù)據(jù)描述符>找不到的屬性觸發(fā)__getattr__()的分析代碼如下:

class Foo():def __get__(self, instance, owner):print('我是get方法') class Bar:x = Foo()def __getattr__(self, item):print('我是getattr方法') b1 = Bar() b1.x #結(jié)果為:我是get方法 b1.name #結(jié)果為:我是getattr方法

上述代碼可以看出找得到時(shí)觸發(fā)__get__方法,找不到就會(huì)觸發(fā)__getattr__方法。說明了非數(shù)據(jù)描述符的優(yōu)先級(jí)高于找不到的屬性觸發(fā)__getattr__()。

4.描述符的應(yīng)用

描述符可以應(yīng)用到哪些場(chǎng)合呢?我們就來舉個(gè)例子,通過描述符機(jī)制來實(shí)現(xiàn)參數(shù)的賦值類型限制。即:

class Typed:def __init__(self,key,expected_type):self.key = keyself.expected_type = expected_typedef __get__(self, instance, owner):print('get方法')return instance.__dict__[self.key]def __set__(self, instance, value):print('set方法')if not isinstance(value,self.expected_type):raise TypeError('你傳入的類型不是%s' %self.expected_type)instance.__dict__[self.key] = valuedef __delete__(self, instance):print('delete方法')instance.__dict__.pop(self.key) class People:name = Typed('name',str)age = Typed('age',int)salary = Typed('salary',float)def __init__(self,name,age,salary):self.name = nameself.age = ageself.salary = salaryp1 = People('alex',18,6666.66) p2 = People(250,26,4568.55) #不符合賦值類型,拋出異常

上述代碼對(duì)象p1是滿足參數(shù)的賦值類型,所以會(huì)觸發(fā)三次__set__方法。而對(duì)象p2不符合賦值類型,就會(huì)拋出異常。

5.propetry

一個(gè)靜態(tài)屬性property本質(zhì)就是實(shí)現(xiàn)了__get__,__set__,__delete__三種方法。

propetry有兩種用法,第一種即:

class Foo:@property #靜態(tài)屬性def AAA(self):print('get的時(shí)候運(yùn)行')@AAA.setterdef AAA(self,value):print('set的時(shí)候運(yùn)行',value)@AAA.deleterdef AAA(self):print('delete的時(shí)候運(yùn)行') f1 = Foo() f1.AAA f1.AAA='aaa' del f1.AAA

執(zhí)行結(jié)果為:

get的時(shí)候運(yùn)行 set的時(shí)候運(yùn)行 aaa delete的時(shí)候運(yùn)行

上述代碼中只有在屬性AAA定義property后才能定義AAA.setter,AAA.deleter。

第二種用法的代碼實(shí)現(xiàn)如下:

class Foo:def get_AAA(self):print('get的時(shí)候運(yùn)行')def set_AAA(self,value):print('set的時(shí)候運(yùn)行',value)def del_AAA(self):print('delete的時(shí)候運(yùn)行')AAA = property(get_AAA,set_AAA,del_AAA) #內(nèi)置property三個(gè)參數(shù)與get,set,delete一一對(duì)應(yīng) f1 = Foo() f1.AAA f1.AAA='aaa' del f1.AAA

執(zhí)行結(jié)果為:

get的時(shí)候運(yùn)行 set的時(shí)候運(yùn)行 aaa delete的時(shí)候運(yùn)行

上述兩種用法的結(jié)果都是一樣的。

我們也可以利用描述符自定制property,實(shí)現(xiàn)延時(shí)計(jì)算。我們先對(duì)一段代碼進(jìn)行分析,來看看我們利用描述符自定制property該如何實(shí)現(xiàn)相同的目的。即:

class Room:def __init__(self,name,width,length):self.name = nameself.width = widthself.length = length@property #area=property(area)def area(self):return self.width*self.length r1 = Room('別墅',15,16) print(r1.area) #結(jié)果為:240

在上述代碼中@property相當(dāng)于實(shí)現(xiàn)了area=property(area),而property它是一個(gè)類,這是不是相當(dāng)于property類定義成Room類的類屬性,這不就是描述符的性質(zhì)嗎?它既然是描述符,那么它是數(shù)據(jù)描述符還是非數(shù)據(jù)描述符呢?從結(jié)果它打印實(shí)例屬性可以看出,它是非數(shù)據(jù)描述符,因?yàn)閷?shí)例屬性的優(yōu)先級(jí)>非數(shù)據(jù)描述符。假如我們自定制property,不采用內(nèi)置的靜態(tài)屬性property,該如何實(shí)現(xiàn)上述代碼,即:

class Lazyproperty:def __init__(self,func):self.func = funcdef __get__(self, instance, owner):return self.func(instance) #instance實(shí)例本身 class Room:def __init__(self,name,width,length):self.name = nameself.width = widthself.length = length@Lazyproperty #area=Lazyproperty(area)def area(self):return self.width*self.length r1 = Room('別墅',15,16) print(r1.area) #結(jié)果為:240

上述代碼中的@Lazyproperty是我自定制的,當(dāng)然Lazyproperty類中我們只能定義成非數(shù)據(jù)描述符,否則不會(huì)執(zhí)行area方法。

我們繼續(xù)利用自定制property來實(shí)現(xiàn)延時(shí)計(jì)算功能:

class Lazyproperty:def __init__(self,func):self.func = funcdef __get__(self, instance, owner):print('get')if instance is None: #被類調(diào)用必須寫,否則報(bào)錯(cuò),因?yàn)轭悰]有實(shí)例return selfres = self.func(instance) #instance實(shí)例本身setattr(instance,self.func.__name__,res) #緩存return res class Room:def __init__(self,name,width,length):self.name = nameself.width = widthself.length = length@Lazyproperty #area=Lazyproperty(area)def area(self):return self.width*self.length r1 = Room('別墅',15,16) print(r1.area) #從字典里先找,因?yàn)閷?shí)例屬性>非數(shù)據(jù)描述符,沒有再去類的中找,然后出發(fā)了area的__get__方法 print(r1.area) #先從自己的屬性字典找,找到了,是上次計(jì)算的結(jié)果,這樣就不用每執(zhí)行一次都去計(jì)算

上述代碼實(shí)現(xiàn)了延時(shí)計(jì)算功能,這樣這不會(huì)每次都打印get的操作。描述符是可以實(shí)現(xiàn)大部分python類特性中的底層魔法,包括@classmethod,@staticmethd,@property甚至是__slots__屬性。

?6.類的裝飾器

類的裝飾器與函數(shù)的裝飾器性質(zhì)是一樣的,類的裝飾器分為無參裝飾器和有參裝飾器。

我們先來定義一個(gè)無參的裝飾器,即:

def deco(func):print('============')func.x = 1func.y = 2return func @deco #相當(dāng)于Foo = deco(Foo) class Foo:pass print(Foo.__dict__)

執(zhí)行結(jié)果為:

============ {'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None, 'x': 1, 'y': 2}

上述代碼當(dāng)程序遇到@deco就馬上執(zhí)行了Foo=deco(Foo),可以在結(jié)果可以看出Foo類中的屬性字典中有鍵值對(duì)x與y。

下面我們來介紹有參的裝飾器該如何定義:

def Typed(**kwargs):def deco(obj):for key,val in kwargs.items():setattr(obj,key,val)return objreturn deco@Typed(x=1,y=2,z=3) #1.Typed(x=1,y=2,z=3)--->deco 2.@deco----->Foo=deco(Foo) class Foo:pass print(Foo.__dict__)@Typed(name='alex') #@deco---->Bar=deco(Bar) class Bar:pass print(Bar.name)

執(zhí)行的結(jié)果為:

{'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None, 'x': 1, 'y': 2, 'z': 3} alex

上述代碼定義了有參的裝飾器,函數(shù)Typed是用來接收所有參數(shù)的。

下面我們利用裝飾器的應(yīng)用以及描述符來實(shí)現(xiàn)參數(shù)的賦值類型限制,即:

class Typed:def __init__(self,key,expected_type):self.key = keyself.expected_type = expected_typedef __get__(self, instance, owner):print('get方法')return instance.__dict__[self.key]def __set__(self, instance, value):print('set方法')if not isinstance(value,self.expected_type):raise TypeError('你傳入的類型不是%s' %self.expected_type)instance.__dict__[self.key] = valuedef __delete__(self, instance):print('delete方法')instance.__dict__.pop(self.key)def deco(**kwargs):def wrapper(obj):for key,val in kwargs.items():setattr(obj,key,Typed(key,val)) #Typed(key,val)描述符return objreturn wrapper@deco(name = str,age = int,salary = float) class People:def __init__(self,name,age,salary):self.name = nameself.age = ageself.salary = salaryp1 = People('alex',20,6666.66)

實(shí)例化時(shí)觸發(fā)了三次__set__方法。在描述符里也規(guī)定了參數(shù)的賦值類型。

轉(zhuǎn)載于:https://www.cnblogs.com/lzc69/p/11316469.html

創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)

總結(jié)

以上是生活随笔為你收集整理的描述符的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。