1. 程式人生 > >Python進階-----自定制property

Python進階-----自定制property

數據 ttr 再次 內置 延遲 pre func sel 延遲計算

一、回顧python內置的裝飾器@property
  @property的作用就是將類的函數屬性同對象的數據屬性一樣可供對象直接調用(靜態屬性),而不需要加()

 1 class Room:
 2     def __init__(self,name,width,length):
 3         self.name = name
 4         self.width = width
 5         self.length = length
 6 
 7     @property           #這個裝飾器可以使得Room實例化的對象直接調用area這個函數屬性
8 def area(self): 9 return %s 的面積是 %d%(self.name,self.length * self.width) 10 r1 = Room(臥室,10,5) 11 print(r1.area) #‘臥室 的面積是 50‘ 12 print(Room.area) #類調用靜態屬性 <property object at 0x000001BEBAF218B8>

二、我們可以通過描述符和類的裝飾器來自己制作類似於上述的property
  裝飾器也可以是類,描述符主要用到__get__方法,返回的就是需要調用函數的返回值

 1 class diy_property:
 2     def __init__(self,func):
 3         self.func = func
 4     def __get__(self, instance, owner):    #存在__get__所以這個diy_property類就是一個描述符
 5         ‘‘‘類來調用,instance為None,owner為類本身,實例來調用,instance為實例,owner為類本身‘‘‘
 6         if instance == None:
 7             return self                    #
仿照property,如果通過類調用靜態屬性,則返回裝飾器的實例對象 8 res = self.func(instance) #===> res = r2.area(被修飾類的self) ===> res = r2.area(r2) 9 return res 10 11 class Room: 12 def __init__(self,name,width,length): 13 self.name = name 14 self.width = width 15 self.length = length 16 17 @diy_property #==> area=diy_property(area) 相當於定義了一個類屬性,即描述符 18 def area(self): 19 return %s 的面積是 %d % (self.name, self.length * self.width) 20 21 r2 = Room(客廳,15,8) 22 print(r2.area) #客廳 的面積是 120 23 print(Room.area) #類調用靜態屬性 <__main__.diy_property object at 0x000001BEBB0AD1D0> 24 # 通過類的裝飾器和描述符基本完成了@property的功能

三、通過自定制property實現延遲計算
  什麽是延遲計算:類屬性的延遲計算就是將類的屬性定義成一個property,只在訪問的時候才會計算,而且一旦被訪問後,結果將會被緩存起來,不用每次都計算。
  所以解決思路就是:將計算的結果存放至實例的屬性字典中,這樣再次訪問這個靜態屬性的時候,會直接從實例的屬性字典中拿取。避免了重復計算。

 1 class Lazy_property:
 2     def __init__(self,func):
 3         self.func = func
 4     def __get__(self, instance, owner):
 5         print(執行我了~~~)
 6         if instance == None:            #如果是類本身在調用靜態屬性時
 7             return self                 #返回裝飾器實例對象
 8         else:
 9             res = self.func(instance)
10             setattr(instance,self.func.__name__,res)   #將靜態屬性的值放入實例的屬性字典中,key是靜態屬性的函數名,value是靜態屬性的值
11             return res
12 
13 class Room:
14     def __init__(self,name,width,length):
15         self.name = name
16         self.width = width
17         self.length = length
18 
19     @Lazy_property
20     def area(self):
21         return %s的面積是%d%(self.name,self.width*self.length)
22 r3 = Room(廚房,4,7)
23 print(r3.area)
24 print(r3.__dict__)
25 #>>>執行我了~~~
26 # >>>廚房的面積是28
27 # >>>{‘name‘: ‘廚房‘, ‘width‘: 4, ‘length‘: 7, ‘area‘: ‘廚房的面積是28‘}
28 
29 #我們再次調用area這個靜態屬性
30 print(r3.area)
31 # >>>‘廚房的面積是28‘

  可以發現,再次調用area這個靜態屬性,程序不會再去調用裝飾器描述符中的__get__方法了,因為我們定義的描述符沒有__set__方法,所以是一個非數據描述符。所以它的優先級低於實例屬性,因為第一次調用area靜態屬性的時候,描述符就將該屬性設置到實例的屬性字典中,所以下一次調用
的時候會優先從實例屬性字典中查找。

Python進階-----自定制property