Python黑魔法@property裝飾器的使用技巧解析

高洛峰
發布: 2017-01-23 14:50:32
原創
1194 人瀏覽過

@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   

可以看到,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: 

Python2.6程式碼 

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: 1、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
登入後複製

在python2.6下,分別運行測試 

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

片段2:正確運行


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

BIF: 

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

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

原來在python2.6下,內建類型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 classs can __c​​rance,與 __getattribute__().

同時我們也可以透過以下方法驗證 
Python 2.6程式碼 

class Parrot(object):
登入後複製

   

Python 2.666 type 'classobj'>,可以看出是我們需要的object類型(python 3.0 將object類作為預設基類,所以都會回傳

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

最後的程式碼將如下: 

class A:
  pass
 
>>type(A)
<type &#39;classobj&#39;>
登入後複製
   

另外,@property是在2.6、3.0

另外,@property是在2.6、3.0

另外,@property是在2.6、3.0

另外,@property是在2.6、3.0

另外,@property是在2.6、3.0新增的,2.5沒有該功能。 🎜🎜更多Python黑魔法@property裝飾器的使用技巧解析相關文章請關注PHP中文網! 🎜
相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板