Python中有一個被稱為屬性函數(property)的小概念,它可以做一些有用的事情。在這篇文章中,我們將看到如何能做以下幾點:
- 將類方法轉換為只讀屬性
- 重新實現一個屬性的setter和getter方法
一、裝飾器
使用屬性函數的最簡單的方法之一是將它作為一個方法的裝飾器來使用。這可以讓你將一個類方法轉變成一個類屬性。當我需要做某些值的合並時,我發現這很有用。其他想要獲取它作為方法使用的人,發現在寫轉換函數時它很有用。讓我們來看一個簡單的例子:
class Person(object): """""" #---------------------------------------------------------------------- def __init__(self, first_name, last_name): """Constructor""" self.first_name = first_name self.last_name = last_name #---------------------------------------------------------------------- @property def full_name(self): """ Return the full name """ return "%s %s" % (self.first_name, self.last_name)
在上面的代碼中,我們創建了兩個類屬性:self.first_name和self.last_name。接下來,我們創建了一個full_name方法,它有一個@property裝飾器。這使我們能夠在Python解釋器會話中有如下的交互:
>>> person = Person("Mike", "Driscoll") >>> person.full_name 'Mike Driscoll' >>> person.first_name 'Mike' >>> person.full_name = "Jackalope" Traceback (most recent call last): File "<string>", line 1, in <fragment> AttributeError: can't set attribute
正如你所看到的,因為我們將方法變成了屬性,我們可以使用正常的點符號訪問它。但是,如果我們試圖將該屬性設為其他值,我們會引發一個AttributeError錯誤。改變full_name屬性的唯一方法是間接這樣做:
>>> person.first_name = "Dan" >>> person.full_name 'Dan Driscoll'
二、使用python property 替代 setter 和 getter 方法
讓我們假設我們有一些遺留代碼,它們是由一些對Python理解得不夠好的人寫的。如果你像我一樣,你之前已經看到過這類的代碼:
from decimal import Decimal ######################################################################## class Fees(object): """""" #---------------------------------------------------------------------- def __init__(self): """Constructor""" self._fee = None #---------------------------------------------------------------------- def get_fee(self): """ Return the current fee """ return self._fee #---------------------------------------------------------------------- def set_fee(self, value): """ Set the fee """ if isinstance(value, str): self._fee = Decimal(value) elif isinstance(value, Decimal): self._fee = value
要使用這個類,我們必須要使用定義的getter和setter方法:
>>> f = Fees() >>> f.set_fee("1") >>> f.get_fee() Decimal('1')
如果你想添加可以使用正常點符號訪問的屬性,而不破壞所有依賴於這段代碼的應用程序,你可以通過添加一個屬性函數非常簡單地改變它:
from decimal import Decimal ######################################################################## class Fees(object): """""" #---------------------------------------------------------------------- def __init__(self): """Constructor""" self._fee = None #---------------------------------------------------------------------- def get_fee(self): """ Return the current fee """ return self._fee #---------------------------------------------------------------------- def set_fee(self, value): """ Set the fee """ if isinstance(value, str): self._fee = Decimal(value) elif isinstance(value, Decimal): self._fee = value fee = property(get_fee, set_fee)
我們在這段代碼的末尾添加了一行。現在我們可以這樣做:
>>> f = Fees() >>> f.set_fee("1") >>> f.fee Decimal('1') >>> f.fee = "2" >>> f.get_fee() Decimal('2')
正如你所看到的,當我們以這種方式使用屬性函數時,它允許fee屬性設置並獲取值本身而不破壞原有代碼。讓我們使用屬性裝飾器來重寫這段代碼,看看我們是否能得到一個允許設置的屬性值。
from decimal import Decimal ######################################################################## class Fees(object): """""" #---------------------------------------------------------------------- def __init__(self): """Constructor""" self._fee = None #---------------------------------------------------------------------- @property def fee(self): """ The fee property - the getter """ return self._fee #---------------------------------------------------------------------- @fee.setter def fee(self, value): """ The setter of the fee property """ if isinstance(value, str): self._fee = Decimal(value) elif isinstance(value, Decimal): self._fee = value #---------------------------------------------------------------------- if __name__ == "__main__": f = Fees()
上面的代碼演示了如何為fee屬性創建一個setter方法。你可以用一個名為@fee.setter的裝飾器裝飾第二個方法名也為fee的方法來實現這個。當你如下所做時,setter被調用:
>>> f = Fees() >>> f.fee = "1"
如果你看屬性函數的說明,它有fget, fset, fdel和doc幾個參數。如果你想對屬性使用del命令,你可以使用@fee.deleter創建另一個裝飾器來裝飾相同名字的函數從而實現刪除的同樣效果。
三、property函數
property() 函數的作用是在新式類中返回屬性值。
class property([fget[, fset[, fdel[, doc]]]]) # fget -- 獲取屬性值的函數 # fset -- 設置屬性值的函數 # fdel -- 刪除屬性值函數 # doc -- 屬性描述信息
class C(object): def __init__(self): self._x = None 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.")
如果 c 是 C 的實例化,
c.x 將觸發 getter,
c.x = value 將觸發 setter ,
del c.x 觸發 deleter。
如果給定 doc 參數,其將成為這個屬性值的 docstring,否則 property 函數就會復制 fget 函數的 docstring(如果有的話)。