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