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創建另一個裝飾器來裝飾相同名字的函數從而實現刪除的同樣效果。