1. 程式人生 > >Python中的property, setter裝飾器

Python中的property, setter裝飾器

1. 問題提出

python中用”.”操作來訪問和改寫類的屬性成員時,會呼叫__get____set__方法,模式情況下,python會查詢class.__dict__字典,對對應值進行操作。比如C.x會呼叫C.__get__訪問最終讀取C.__dict__[x]元素。

如果需要讀取時對輸出結果進行修飾或者對輸入進行合法化檢查,通常做法是自己寫get和set函式,並通過調get和set函式進行讀寫類成員屬性。

例子:

class Timer:

  def __init__(self, value = 0.0):
    self.time = value
    self.unit = 's'
def get_time(self): return str(self.time) + ' ' + self.unit def set_time(self, value): if(value < 0): raise ValueError('Time cannot be negetive.') self.time = value t = Timer() t.set_time(1.0) t.get_time()

但是這樣並不美觀,那有沒有美顏的辦法呢?當然!

2. 解決方案

在變數x前面加下劃線_表示為私有變數_x,並將變數名x設為用property

函式返回的物件(property object)。

property函式的宣告為

def property(fget = None, fset = None, fdel = None, doc = None) -> <property object>

其中fget, fset, fdel對應變數操作的讀取(get),設定(set)和刪除(del)函式。
property物件<property object>有三個類方法,即setter, getterdelete,用於之後設定相應的函式。

3.實現效果

在操作類私成員的時候呼叫__get____set__

方法時,不再採用預設的讀取字典的方法,而使用propertyfgetfset指定的方法,從而實現了方便的賦值操作。
注意,即使在__init__函式中呼叫賦值語句,也使用的是fset方法。

例子:

class Timer:

  def __init__(self, value = 0.0):
    # 1. 將變數加"_",標誌這是私有成員
    self._time = value
    self._unit = 's'

  def get_time(self):
    return str(self._time) + ' ' + self._unit

  def set_time(self, value):
    if(value < 0):
      raise ValueError('Time cannot be negetive.')
    self._time = value

  # 將變數名賦值為包含get和set方法的property物件
  time = property(get_time, set_time)

t = Timer()
t.time = 1.0
print(t.time)

這樣的訪問和設定和賦值語句一樣非常自然了。

上面的例子中,如果採用property物件的方法而不是用property函式,那麼

# time = property(get_time, set_time)
# =========將變成==============>>>
time = property()
time = time.getter(get_time)
time = time.setter(set_time)

4. property裝飾器

如果覺得在最後加property函式這樣寫類的時候還是不自然,容易忘會寫錯,那麼裝飾器可以讓類的書寫帥到飛起。

例子:

class Timer:

  def __init__(self, value = 0.0):
    self._time = value
    self._unit = 's'

  # 使用裝飾器的時候,需要注意:
  # 1. 裝飾器名,函式名需要一直
  # 2. property需要先宣告,再寫setter,順序不能倒過來
  @property
  def time(self):
    return str(self._time) + ' ' + self._unit

  @time.setter
  def time(self, value):
    if(value < 0):
      raise ValueError('Time cannot be negetive.')
    self._time = value

t = Timer()
t.time = 1.0
print(t.time)

這兩個裝飾器
@property裝飾器會把成員函式x轉換為getter,相當於做了x = property(); x = x.getter(x_get)
@x.setter裝飾器會把成員函式x轉換為setter,相當於做了x = x.seter(x_set).

可以看到我們實現了通過屬性x來對私有變數_x進行操作。

參考資料