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

@property裝飾器能把一個方法變成屬性一樣來調用,下面我們就一起來看看Python的黑魔法@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

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

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

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的機制應該很好理解。

在這裏,懶惰類有get方法,說明是個描述器,第一次執行c.area的時候,因爲順序問題,先去Ç. dict中找,沒找到,就去類空間找,在類圈中,有面積()方法,於是就被get攔截。

get中,調用實例的區域()方法算出結果,並動態給實例添加個同名屬性把結果賦給它,即加到Ç. dict中去。

再次執行c.area的時候,先去Ç. dict找,因爲此時已經有了,就不會經過區域()方法和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(對象):

在python2下,分別運行測試

片段1:將會提示一個預期的錯誤信息AttributeError:無法設置屬性

片段2:正確運行

參考python2文檔,@ property將提供一個ready-only屬性,以上代碼沒有提供對應的@ voltage.setter,按理說片段2代碼將提示運行錯誤,在python2文檔中,我們可以找到以下信息:

BIF:

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

返回新樣式類的屬性屬性(從對象派生的類)。

原來在python2下,內置類型對象並不是默認的基類,如果在定義類時,沒有明確說明的話(代碼片段2),我們定義的Parrot(代碼片段2)將不會繼承對象

而對象類正好提供了我們需要的@property功能,在文檔中我們可以查到如下信息:

新式課

任何繼承自object的類。這包括所有內置類型,如list和dict。只有新式類可以使用Python的更新,通用的功能,如slots,描述符,屬性和getattribute ()。

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

class A: 
  pass 
>>type(A) 
<type 'classobj'>
class A(object): 
  pass 
>>type(A) 
<type 'type'>

從返回的<type'classobj'>,<type'type'>可以看出<type'type'>是我們需要的對象類型(python 3.0將對象類作爲默認基類,所以都將返回<type'type “>)

爲了考慮代碼的python版本過渡期的兼容性問題,我覺得應該定義類文件的時候,都應該顯式定義對象,做爲一個好習慣

最後的代碼將如下:

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

python高深莫測, Python學習交流q.u.n【 784758214】羣內有安裝包和學習視頻資料,零基礎,進階,實戰免費的在線直播免費課程,希望可以幫助你快速瞭解Python

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章