property() 函式


你定義了一個Ball類別:
class Ball:
def __init__(self, radius):
self.radius = radius

你可以建立實例並直接存取radius特性:
ball = Ball(1.23)
print(ball.radius)
ball.radius = 2.31

如果這個Ball類別被到處使用,現在你打算修改一下Ball類別,檢查半徑設定不得為小於0,若你這麼修改:
class Ball:
def __init__(self, radius):
if radius <= 0:
raise ValueError('必須是正數')
self.__radius = radius

def getRadius(self):
return self.__radius
def setRadius(self, radius):
if radius <= 0:
raise ValueError('必須是正數')
self.__radius = radius

那麼,你就必須打開每個原先使用到Ball類別的程式碼檔案並加以修改為:
ball = Ball(1.23)
print(ball.getRadius())
ball.setRadius(2.31)

事實上在Python中,你可以直接使用property()函式來修改Ball類別。例如:
class Ball:
def __init__(self, radius):
if radius <= 0:
raise ValueError('必須是正數')
self.__radius = radius

def getRadius(self):
return self.__radius

def setRadius(self, radius):
self.__radius = radius

def delRadius(self):
del self.__radius

radius = property(getRadius, setRadius, delRadius, 'radius 特性說明')

那麼你就可以如下存取radius特性:
ball = Ball(1.23)
print(ball.radius)
ball.radius = 2.31

對於radius的存取,會分別轉發呼叫property()所對應的getRadius、setRadius、delRadius所參考的方法,對於使用Ball類別的客戶端而言,並不用作出修改,亦可以達到以上控制存取目的。

property()函式也可以使用修飾器語法,讓程式更為直覺。例如上例修改為以下:
class Ball:
def __init__(self, radius):
if radius <= 0:
raise ValueError('必須是正數')
self.__radius = radius

@property
def radius(self):
return self.__radius

@radius.setter
def radius(self, radius):
self.__radius = radius

@radius.deleter
def radius(self):
del self.__radius

當然,特性的傳回值,並不一定得有對應的內部資料成員,也可以是計算的結果。例如:
import math
class Ball:
def __init__(self, radius):
if radius <= 0:
raise ValueError('必須是正數')
self.__radius = radius

@property
def radius(self):
return self.__radius

@property
def volumn(self):
return 4.0 / 3.0 * math.pi * self.__radius ** 3

@volumn.setter
def volumn(self, volumn):
if volumn <= 0:
raise ValueError('必須是正數')
self.__radius = ((3.0 * volumn) / (4.0 * math.pi)) ** (1.0 / 3.0)

ball = Ball(10.0)
print(ball.volumn)
ball.volumn = 5000
print(ball.radius)

上例中,實際上並沒有volume對應的內部資料成員,而是將計算對應至__radius或者是從__radius計算出volume。