1. 程式人生 > >Python property 與get/set方法詳解

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各種類庫的原始碼中,經常會遇到很多類似的程式碼結構。