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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

描述符

發布時間:2023/12/2 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 描述符 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
描述符

描述符也是面向進階的一種,由于它的涉及比較廣,所以單獨講。

一、描述符

描述符本質就是一個新式類,在這個新式類中,至少實現了__get__(),__set__(),__delete__()中的一個,這也被稱為描述符協議。

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

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

1.描述符格式

class Foo: #在python3中任何類都是新式類,它只要實現了三種方法之一,那么這個類就被稱作一個描述符def __get__(self, instance, owner): #調用一個屬性時觸發passdef __set__(self, instance, value): #為一個屬性賦值時觸發passdef __delete__(self, instance): #采用del刪除屬性時觸發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() #把描述符代理一個類的屬性def __init__(self,n): self.x = nb1 = Bar(10) b1.x = 11111 b1.x del b1.x

執行代碼結果為:

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

可以看出在實例化時也會觸發__set__方法,因為在類Bar的初始化函數中self.x是被描述符代理的屬性。那么對象b1中的屬性字典中到底存不存在x這個值呢?具體代碼如下所示:

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__)

執行結果為:

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

這是什么情況?無論是實例化操作還是賦值操作,在對象b1的屬性字典中仍然為空。正是因為描述符的關系,它相當于把被描述符的類的調用屬性操作、賦值操作、刪除操作都賦予另一個類來實現,跟本身類并沒有關系,當然這關系到優先級的問題。

3.描述符的優先級

我們要嚴格遵守優先級:類屬性>數據描述符>實例屬性>非數據描述符>找不到的屬性觸發__getattr__()。

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

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) #結果為:111

上述代碼打印結果仍為111,并沒有執行數據描述符,是因為Bar類調用了本來要被描述符代理的屬性x進行了修改。所有類屬性的優先級比數據描述符高。

(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()b1 = Bar() b1.x #結果為:我是get方法 b1.x = 111 #結果為:我是set方法

上述代碼Foo類被定義成Bar類的類屬性,即對Bar類進行實例化,實例屬性只執行數據描述符的方法。說明了數據描述符的優先級高于實例屬性。

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

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

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

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

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

上述代碼可以看出找得到時觸發__get__方法,找不到就會觸發__getattr__方法。說明了非數據描述符的優先級高于找不到的屬性觸發__getattr__()。

4.描述符的應用

描述符可以應用到哪些場合呢?我們就來舉個例子,通過描述符機制來實現參數的賦值類型限制。即:

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) #不符合賦值類型,拋出異常

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

5.propetry

一個靜態屬性property本質就是實現了__get__,__set__,__delete__三種方法。

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

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

執行結果為:

get的時候運行 set的時候運行 aaa delete的時候運行

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

第二種用法的代碼實現如下:

class Foo:def get_AAA(self):print('get的時候運行')def set_AAA(self,value):print('set的時候運行',value)def del_AAA(self):print('delete的時候運行')AAA = property(get_AAA,set_AAA,del_AAA) #內置property三個參數與get,set,delete一一對應 f1 = Foo() f1.AAA f1.AAA='aaa' del f1.AAA

執行結果為:

get的時候運行 set的時候運行 aaa delete的時候運行

上述兩種用法的結果都是一樣的。

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

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) #結果為:240

在上述代碼中@property相當于實現了area=property(area),而property它是一個類,這是不是相當于property類定義成Room類的類屬性,這不就是描述符的性質嗎?它既然是描述符,那么它是數據描述符還是非數據描述符呢?從結果它打印實例屬性可以看出,它是非數據描述符,因為實例屬性的優先級>非數據描述符。假如我們自定制property,不采用內置的靜態屬性property,該如何實現上述代碼,即:

class Lazyproperty:def __init__(self,func):self.func = funcdef __get__(self, instance, owner):return self.func(instance) #instance實例本身 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) #結果為:240

上述代碼中的@Lazyproperty是我自定制的,當然Lazyproperty類中我們只能定義成非數據描述符,否則不會執行area方法。

我們繼續利用自定制property來實現延時計算功能:

class Lazyproperty:def __init__(self,func):self.func = funcdef __get__(self, instance, owner):print('get')if instance is None: #被類調用必須寫,否則報錯,因為類沒有實例return selfres = self.func(instance) #instance實例本身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) #從字典里先找,因為實例屬性>非數據描述符,沒有再去類的中找,然后出發了area的__get__方法 print(r1.area) #先從自己的屬性字典找,找到了,是上次計算的結果,這樣就不用每執行一次都去計算

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

?6.類的裝飾器

類的裝飾器與函數的裝飾器性質是一樣的,類的裝飾器分為無參裝飾器和有參裝飾器。

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

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

執行結果為:

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

上述代碼當程序遇到@deco就馬上執行了Foo=deco(Foo),可以在結果可以看出Foo類中的屬性字典中有鍵值對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)

執行的結果為:

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

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

下面我們利用裝飾器的應用以及描述符來實現參數的賦值類型限制,即:

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)

實例化時觸發了三次__set__方法。在描述符里也規定了參數的賦值類型。

轉載于:https://www.cnblogs.com/lzc69/p/11316469.html

創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

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

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