首頁  >  文章  >  後端開發  >  Python裝飾器之property用法詳解

Python裝飾器之property用法詳解

巴扎黑
巴扎黑原創
2017-08-18 17:11:532267瀏覽

@property裝飾器能把一個方法變成屬性一樣來調用,下面我們就一起來看看Python黑魔法@property裝飾器的使用技巧解析

@property有什麼用呢?表面看來,就是將一個方法用屬性的方式來訪問.

上代碼,代碼最清晰了.

class Circle(object): 
  def __init__(self, radius): 
    self.radius = radius 
 
  @property 
  def area(self): 
    return 3.14 * self.radius ** 2 
 
c = Circle(4) 
print c.radius 
print c.area

可以看到,area雖然是定義成一個方法的形式,但是加上@property後,可以直接c.area,當成屬性訪問.

現在問題來了,每次調用c.area,都會計算一次,太浪費cpu了,怎樣才能只計算一次呢?這就是lazy property.

class lazy(object): 
  def __init__(self, func): 
    self.func = func 
 
  def __get__(self, instance, cls): 
    val = self.func(instance) 
    setattr(instance, self.func.__name__, val) 
    return val 
 
class Circle(object): 
  def __init__(self, radius): 
    self.radius = radius 
 
  @lazy 
  def area(self): 
    print 'evalute' 
    return 3.14 * self.radius ** 2 
 
c = Circle(4) 
print c.radius 
print c.area 
print c.area 
print c.area

可以看到,'evalute'只輸出了一次,對@lazy的機制應該很好理解.

在這裡,lazy類別有__get__方法,說明是個描述器,第一次執行c.area的時候,因為順序問題,先去c.__dict__中找,沒找到,就去類空間找,在類Circle中,有area()方法,於是就被__get__攔截.

在__get__中,呼叫實例的area()方法算出結果,並動態給實例添加個同名屬性把結果賦給它,即加到c.__dict__中去.

再執行c.area的時候,先去c.__dict__找,因為此時已經有了,就不會經過area()方法和__get__了.

注意點

請注意以下程式碼場景:

程式碼片段1:  

class Parrot(object): 
  def __init__(self): 
    self._voltage = 100000 
 
  @property 
  def voltage(self): 
    """Get the current voltage.""" 
    return self._voltage 
 
if __name__ == "__main__": 
  # instance 
  p = Parrot() 
  # similarly invoke "getter" via @property 
  print p.voltage 
  # update, similarly invoke "setter" 
  p.voltage = 12

程式碼片段2:  

class Parrot: 
  def __init__(self): 
    self._voltage = 100000 
 
  @property 
  def voltage(self): 
    """Get the current voltage.""" 
    return self._voltage 
 
if __name__ == "__main__": 
  # instance 
  p = Parrot() 
  # similarly invoke "getter" via @property 
  print p.voltage 
  # update, similarly invoke "setter" 
  p.voltage = 12

程式碼1、2的區別在於 

class Parrot(object): 

在python2下,分別執行測試 

片段1:將會提示一個預期的錯誤訊息AttributeError: can't set attribute

片段2:正確運行

參考python2文檔,@property將提供一個ready-only property,以上程式碼沒有提供對應的@voltage.setter,按理說片段2程式碼將提示執行錯誤,在python2文件中,我們可以找到以下資訊:

BIF: 

property([fget[, fset[, fdel[, doc]]]]) 

Return a property attribute for new-style classes (classes that derive from object). 

原來在python2下,內建類型object 並不是預設的基類,如果在定義類別時,沒有明確說明的話(程式碼片段2),我們定義的Parrot(程式碼片段2)將不會繼承object

而object類別正好提供了我們需要的@property功能,在文件中我們可以查到以下資訊:

new-style class 

Any class which inherits from object. This includes all built-in types like list and dict. Only new-style classes can use,'s newer, versatile features like slots__ properties, and __getattribute__().

同時我們也可以透過以下方法來驗證 

class A: 
  pass 
>>type(A) 
<type &#39;classobj&#39;>
class A(object): 
  pass 
>>type(A) 
<type &#39;type&#39;>

從傳回的635a4e04c7149d4983c9db32b6c8ae2d,f5ae6bbdf402bfea04a71faa8c02de77可以看出f5ae6bbdf402bfea04a71faa8c02de77是我們需要的object類型(python 3.0 將object類作為預設基類,所以都會回傳f5ae6bbdf402bfea04a71faa8c02de77)

#為了考慮程式碼的python 版本過渡期的兼容性問題,我覺得應該定義class檔案的時候,都應該明確定義object,做為一個好習慣

最後的程式碼將如下: 

class Parrot(object): 
  def __init__(self): 
    self._voltage = 100000 
  @property 
  def voltage(self): 
    """Get the current voltage.""" 
    return self._voltage 
  @voltage.setter 
  def voltage(self, new_value): 
    self._voltage = new_value 
 
if __name__ == "__main__": 
  # instance 
  p = Parrot() 
  # similarly invoke "getter" via @property 
  print p.voltage 
  # update, similarly invoke "setter" 
  p.voltage = 12

另外,@property是在2.6、3.0新增的,2.5沒有此功能。 

#

以上是Python裝飾器之property用法詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn