今天在 trace 一個 python 專案時,看到類似 getter/setter 函式,不過實際使用時卻不像其他語言的 getter/setter 用法。於是就去 google 一下,找到 @property 的用法。
原來,在 python class 中,因為沒有 private 變數,全部都是 public ,任何人都可以存取,頂多只有透過命名風格在變數前加 ‘_‘ 或 ‘__‘ ,讓別人了解這個變數是給內部使用,不建議直接呼叫。也因此 getter/setter 的設計模式在 python 較沒意義,取而代之的是使用 @property 裝飾器(decorator)來達到類似的效果。
範例 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| class Example(object): _text = 'This is an origin string' @property def text(self): return self._text @text.setter def text(self, string): if isinstance(string, str): self._text = string else: raise TypeError('%s url must be str, got %s:' % (type(self).__name__, type(url).__name__)) if __name__ == '__main__': ex = Example() string = 'This is a test' print 'Get Example.text: %s' % (ex.text) print 'Set Example.text: %s' % (string) ex.text = string print 'Get Example.text: %s' % (ex.text)
|
執行結果
1 2 3 4
| $ ./decorator_property.py Get Example.text: This is an origin string Set Example.text: This is a test Get Example.text: This is a test
|
不過在 trace 的 python 專案中,用的是另一種方法。
範例 2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| class Example(object): _text = 'This is an origin string' def _get_text(self): return self._text def _set_text(self, string): if isinstance(string, str): self._text = string else: raise TypeError('%s url must be str, got %s:' % (type(self).__name__, type(url).__name__)) text = property(_get_text, _set_text) if __name__ == '__main__': ex = Example() string = 'This is a test' print 'Get Example.text: %s' % (ex.text) print 'Set Example.text: %s' % (string) ex.text = string print 'Get Example.text: %s' % (ex.text)
|
執行結果
1 2 3 4
| $ ./decorator_property_2.py Get Example.text: This is an origin string Set Example.text: This is a test Get Example.text: This is a test
|
可以看到兩種寫法都可以達到相同的效果,而且用 type(ex.text)
看型態都是 <type 'str'>
。
至於哪種寫法比較好?就本人覺得,比較偏好範例 2 ,畢竟可以對應到其他語言的寫法,相較之下可讀性較高。
References
- 使用@property
- Python @property versus getters and setters