取得屬性值與設定它【使用特性】

  • 有些物件導向語言會提供私有物件屬性
    • 無法在外部存取它們
    • 程式員通常必須編寫 gettersetter 方法來讀取與寫入這種「私有屬性值」
  • Python 不需要 getter 與 setter,因為所有屬性與方法都是公用的,你必須為自己的行為負責
    • 如果你不敢直接存取屬性,當然也可以編寫 getter 與 setter
    • 但是請採取 Python 的風格 → 使用特性(property)

【定義特性:方法一】定義一個 Duck 類別,它只有一個屬性:hidden_name

  1. 定義一個 Duck 類別,它只有一個屬性:hidden_name(本書下一節,會教更好的屬性命名方式,讓你保持私用
  2. 不希望有人直接存取它,所以會定義兩個方法:一個 getter(get_name())與一個 setter(set_name())
    • 在這兩個方法裡面加入一個 print() 陳述式 → 來顯示它們被呼叫
    • 最後,將這些方法定義為 name 屬性的特性
>>> class Duck():
    def __init__(self, input_name):
        self.hidden_name = input_name
    def get_name(self):
        print('inside the getter')
        return self.hidden_name
    def set_name(self, input_name):
        print('inside the setter')
        self.hidden_name = input_name
    name = property(get_name, set_name)

* 在最後一行之前,新方法的行為就像是一般的 getter 與 setter,它將這兩個方法定義成 name 屬性的特性
* 第一個傳入 property() 的引數是 getter 方法,第二個是 setter

【範例】參考物件及呼叫

# 當參考任何 Duck 物件的 name 時 → 它其實會呼叫 get_name() 方法,並回傳它
>>> 文字1 = Duck('引數')
>>> 文字1.name
inside the getter
'引數'

# 可以直接呼叫 get_name(),就像一般的 getter 方法一樣
>>> 文字1.get_name()
inside the getter
'引數'

# 將一個值指派給 name 屬性時,就會呼叫 set_name() 方法
>>> 文字1.name = '文字2'
inside the setter
>>> 文字1.name
inside the getter
'文字2'

# 還是可以直接呼叫 set_name() 方法
>>> 文字1.set_name('文字2')
inside the setter
>>>文字1.name
inside the getter
'文字2'
>>> class Duck():
    def __init__(self, input_name):
        self.hidden_name = input_name
    def get_name(self):
        print('inside the getter')
        return self.hidden_name
    def set_name(self, input_name):
        print('inside the setter')
        self.hidden_name = input_name
    name = property(get_name, set_name)

# 當參考任何 Duck 物件的 name 時,它其實會呼叫 get_name() 方法,並回傳它
>>> fowl = Duck('Howard')
>>> fowl.name
inside the getter
'Howard'

# 還是可以直接呼叫 get_name(),就像一般的 getter 方法一樣
>>> fowl.get_name()
inside the getter
'Howard'

# 當將一個值指派給 name 屬性時,就會呼叫 set_name() 方法
>>> fowl.name = 'Daffy'
inside the setter
>>> fowl.name
inside the getter
'Daffy'

# 還是可以直接呼叫 set_name() 方法
>>> fowl.set_name('Daffy')
inside the setter
>>> fowl.name
inside the getter
'Daffy'

【定義特性:方法二】

  • 另一個定義特性的方法,就是使用裝飾器
  • 定義兩個不同的方法,它們都叫 name(),但會在前面放上不同的裝飾器
    • @property,在 getter 方法之前
    • @name.setter,在 setter 方法之前
>>> class Duck():
        def __init__(self, input_name):
            self.hidden_name = input_name
        @peoperty
        def name(self)
            print('inside the getter')
            return self.hidden_name
        @name.setter
        def name(self, input_name):
            print('inside the setter')
            self.hidden_name = input_name


# 可以將 name 當成屬性來存取,但這裡看不到 get_name() 或 set_name() 方法
# 呼叫的是hidden_name 屬性,他們仍然可以使用 文字1.hidden_name 來直接讀取與寫入它
# 在下一節,會看到 Python 提供特殊的私用屬性命名方式
>>> 文字1 = Duck('引數')
>>> 文字1.name
inside the getter
'引數'

>>> 文字1.name = '文字3'
inside the setter

>>> 文字1.name
inside the getter
'文字3'
>>> class Duck():
    def __init__(self, input_name):
        self.hidden_name = input_name
    @property
    def name(self):
        print('inside the getter')
        return self.hidden_name
    @name.setter
    def name(self, input_name):
        print('inside the setter')
        self.hidden_name = input_name

# 還是可以將 name 當成屬性來存取,但這裡看不到 get_name() 或 set_name() 方法
>>> fowl = Duck('Howard')
>>> fowl.name
inside the getter
'Howard'
>>> fowl.name = 'Donald'
inside the setter
>>> fowl.name
inside the getter
'Donald'

特性也可以參考算出來的值

  • 在之前的兩個例子中,使用「name 特性」來參考一個「被存在物件裡面的屬性」(這裡稱為 hidden_name)
  • 特性也可以參考算出來的值

【範例】

比起直接存取屬性,使用特性還有一個很大的好處: 當你更改屬性的定義時,只要修改類別定義裡面的程式就可以了,不需要修改所有呼叫方

# 定義一個 Circle 類別,它有一個 radius 屬性,與一個算出來的 diameter 特性
>>> class Circle():
        def __init__(self, radius):
            self.radius = radius
        @property
        def diameter(self):
            return 2 * self.radius


# 建立一個 Circle 物件,並且為 radius 設一個初始值
>>> c = Circle(初始值)
>>> c.radius
初始值


# 像 radius 一樣,把 diameter 當成屬性來參考
>> c.diameter
初始值*2


# 以下是很有趣的地方:可以隨時更改 radius 屬性,它會用目前的 radius 值來計算直徑屬性
>>> c.radius = 數字1
>>> c.diameter
數字*2

# 如果沒有指定 setter 特性給某個屬性,就不能在外面設定它 → 這對唯讀的屬性來說很方便
>>> c.diameter = 數字2
錯誤
# 定義一個 Circle 類別,它有一個 radius 屬性,與一個算出來的 diameter 特性
>>> class Circle():
    def __init__(self, radius):
        self.radius = radius
    @property
    def diameter(self):
        return 2 * self.radius

# 建立一個 Circle 物件,並且為 radius 設一個初始值
>>> c = Circle(5)
>>> c.radius
5

# 可以像 radius 一樣,把 diameter 當成屬性來參考
>>> c.diameter
10

# 以下是很有趣的地方:可以隨時更改 radius 屬性,它會用目前的 radius 值來計算直徑屬性
>>> c.radius = 7
>>> c.diameter
14

# 如果沒有指定 setter 特性給某個屬性,就不能在外面設定它。這對唯讀的屬性來說很方便
>> c.diameter = 20
Traceback (most recent call last):
  File "<pyshell#77>", line 1, in <module>
    c.diameter = 20
AttributeError: can't set attribute

results matching ""

    No results matching ""