Python property 與get/set方法詳解
1.java裡get/set方法
大部分的同學開始寫java程式碼的時候,最初始的程式碼肯定是欄位的get/set方法。大家對於java特別冗長的詬病,很大一部分來自於無處不在的get/set方法。甚至國內有相當一部分不負責任的java書籍,裡面靠大段的get/set程式碼來拼湊篇幅。。。
來個最簡單的例子,估計大家都寫過類似的程式碼:
public class Person { private int age; public Person(int age) { this.age = age; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
2.python裡的get/set方法
python裡如果按java的那一套寫法,程式碼應該像是這樣:
class Person(object): def __init__(self): self._age = None def get_age(self): return self._age def set_age(self,age): if(isinstance(age,str)): self._age = int(age) elif(isinstance(age,int)): self._age = age
顯然這是java風格的程式碼,python的哲學是簡潔明瞭,這麼麻煩的程式碼顯然不是我們想要的。如果我們想直接訪問上面Person類的age屬性,也得使用get/set方法,否則會報錯:
p = Person()
p.set_age("18")
print p.get_age() #OK,沒問題
print p.age #會報錯,'Person' object has no attribute 'age'
3.property方法
如果我們想在py中直接訪問屬性,比如想在上面的例子中直接訪問Person的age欄位,可以在Person類的最後加如下一行程式碼:
age = property(get_age,set_age)
這樣,我們就直接可以訪問age欄位了:
p = Person()
p.age = 18
print p.get_age() #OK,沒問題,返回的結果是18
print p.age #OK,沒問題,返回的結構也是18
上面是用函式模式的方式來使用property的。當然我們也可以用裝飾器的模式使用。
class Person(object):
def __init__(self):
self._age = None
@property
def age(self):
return self._age
@age.setter
def age(self,age):
if isinstance(age,str):
self._age = int(age)
elif isinstance(age,int):
self._age = age
@age.deleter
def age(self):
del self._age
p = Person()
p.age = "18"
print p.age #18
del p.age
print p.age #報錯,AttributeError: 'Person' object has no attribute '_age'
上面使用property裝飾器模式的時候注意幾個小點:
1.三個函式的名字與欄位名是一樣的。
2.使用proterty可以比較簡單實現欄位的讀寫控制。例如想要欄位為只讀屬性,那麼只需要提供getter方法,上面的setter方法可以去掉。
4.用property定義需要計算的屬性
同時我們還可以用property來定義需要進行計算的欄位。這些欄位不會儲存在物件中,只有當我們實際需要的時候才會完成真正計算。
class Person(object):
def __init__(self):
self._age = None
@property
def age(self):
return self._age
@age.setter
def age(self,age):
if isinstance(age,str):
self._age = int(age)
elif isinstance(age,int):
self._age = age
@property
def height(self):
return self.age * 10
p = Person()
p.age = ("18")
print p.height #180
上面的例子,就是根據年齡來推算身高。。
5.property的基本原理
在python中,property()是一個內建的函式。他的原型如下:
def __init__(self, fget=None, fset=None, fdel=None, doc=None): # known special case of property.__init__
"""
property(fget=None, fset=None, fdel=None, doc=None) -> property attribute
fget is a function to be used for getting an attribute value, and likewise
fset is a function for setting, and fdel a function for del'ing, an
attribute. Typical use is to define a managed attribute x:
class C(object):
def getx(self): return self._x
def setx(self, value): self._x = value
def delx(self): del self._x
x = property(getx, setx, delx, "I'm the 'x' property.")
Decorators make defining new properties or modifying existing ones easy:
class C(object):
@property
def x(self):
"I am the 'x' property."
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
# (copied from class doc)
"""
pass
在python的原始碼中,我們就很容易看出property的用法。其中,fget是一個獲取欄位值的函式,而fget是一個設定欄位值的函式,fdel是一個刪除屬性的函式,doc是一個字串(類似於註釋)。從函式實現上看,這些函式引數都是可選的,預設為None,呼叫的其實就是get,set,del方法!
age = property(get_age,set_age)
這句程式碼,其實可以被分解為
age = property()
age = age.getter(get_age)
age = age.setter(set_age)
不去定義名字get_age和set_age,因為他們不是必須的,並且汙染類的名稱空間。而通過property的實現方式,都很簡單。在python各種類庫的原始碼中,經常會遇到很多類似的程式碼結構。