Python 中有一些特別的方法 (method) ,這些方法的識別字 (identifier) 前後都用兩個底線圍起來
每一種都有特定的功能,其中的 __init__() 方法就是物件 (object) 建立時所執行的方法,舉例如下
001 | class Demo: |
002 | def __init__(self, v1=11, v2=22): |
003 | self.__a = v1 |
004 | self.__b = v2 |
005 | |
006 | def do_something(self): |
007 | return self.__a + self.__b |
008 | |
009 | d = Demo(12, 34) |
010 | print(d.do_something()) |
011 | |
012 | # 檔名: class_demo6.py |
013 | # 作者: Kaiching Chang |
014 | # 時間: July, 2014 |
這裡把原本的 set_att() 改成 __init__()
002 | def __init__(self, v1=11, v2=22): |
003 | self.__a = v1 |
004 | self.__b = v2 |
建立 Demo 型態 (type) 的變數 (variable) d 時就像呼叫 set_att() 一樣,可以直接提供參數設定屬性 (attribute) __a 及 __b
009 | d = Demo(12, 34) |
執行結果如下
屬性都已封裝 (encapsulate) ,如果我們還是有需要在外部程式設定屬性值的話該怎麼辦呢?這就要替每個屬性設定各自的 setter 與 getter 方法了,例如
001 | class Demo: |
002 | def __init__(self, v1=11, v2=22): |
003 | self.__a = v1 |
004 | self.__b = v2 |
005 | |
006 | def get_a(self): |
007 | return self.__a |
008 | |
009 | def get_b(self): |
010 | return self.__b |
011 | |
012 | def set_a(self, value): |
013 | self.__a = value |
014 | |
015 | def set_b(self, value): |
016 | self.__b = value |
017 | |
018 | def do_something(self): |
019 | return self.__a + self.__b |
020 | |
021 | d = Demo(11) |
022 | print(d.do_something()) |
023 | d.set_a(5) |
024 | print(d.do_something()) |
025 | |
026 | # 檔名: class_demo7.py |
027 | # 作者: Kaiching Chang |
028 | # 時間: July, 2014 |
setter 是設定屬性的方法, getter 則是取得屬性的方法,上面 6 到 16 行就是定義 setter 與 getter 方法的地方,程式執行結果如下
如果是用 @property 、 @a.setter 與 @b.setter 標記 setter 與 getter 的話,程式行為就會回到預設的情況。
下面我們討論設計 __init__() 方法的技巧,同樣也適用在函數 (function) 的參數列 (parameter list) 。一個簡單的問題,參數 (parameter) 可以有預設值,這樣建立物件時無須提供參數,可是預設值不見得每一個都可以設定一樣,可是有時候我們只是想要把兩個屬性設定成相同的值說。
如果按照我們上面的設計,兩個屬性都想設定一樣的值,就變成兩個參數都得提供
d = Demo(80, 80) |
更麻煩的是如果希望兩個屬性都跟某個預設值一樣,仍然是兩個參數都得提供
d = Demo(11, 11) |
可不可以簡單一點呢?可以的,我們可以把第二個參數的預設值改成 None ,然後加入條件判斷,如下
001 | class Demo: |
002 | def __init__(self, v1=11, v2=None): |
003 | if v2 is None: |
004 | self.__a = v1 |
005 | self.__b = v1 |
006 | else: |
007 | self.__a = v1 |
008 | self.__b = v2 |
009 | |
010 | def do_something(self): |
011 | return self.__a + self.__b |
012 | |
013 | d1 = Demo() |
014 | print(d1.do_something()) |
015 | d2 = Demo(13, 12) |
016 | print(d2.do_something()) |
017 | |
018 | # 檔名: class_demo8.py |
019 | # 作者: Kaiching Chang |
020 | # 時間: July, 2014 |
這樣的技巧在其他強型態的物件導向程式語言中被稱為多載 (overload) ,也就是函數可以定義多個參數版本,因為這些語言所有參數的型態都是固定的,如果需要不同的型態就得宣告其他參數版本的函數或方法。
None 為識別字之一,表示什麼都不是的物件。其中 __init__() 方法
002 | def __init__(self, v1=11, v2=None): |
003 | if v2 is None: |
004 | self.__a = v1 |
005 | self.__b = v1 |
006 | else: |
007 | self.__a = v1 |
008 | self.__b = v2 |
關鍵字 is 用來判斷兩個物件是否相等,這裡為判斷第二個參數 v2 是否為 None ,如果 v2 是 None ,表示呼叫端沒有提供第二個參數,因此把兩個屬性都設定為 v1 ,反之 __a 設定成 v1 , __b 設定為 v2 。
執行結果如下
類別 (class) 的介紹到這裡先到一個段落了,由於一個 Python 檔案就是一個獨立的模組 (module) ,眾多模組集合起來就是個模組庫 (library) 了,接下來我們繼續討論模組囉!
中英文術語對照
方法 | method |
識別字 | identifier |
物件 | object |
型態 | type |
變數 | variable |
屬性 | attribute |
封裝 | encapsulate |
函數 | function |
參數列 | parameter list |
參數 | parameter |
多載 | overload |
類別 | class |
模組 | module |
模組庫 | library |
重點整理
- __init__() 方法是物件建立時所執行的方法,主要用來初始化實體屬性。
- 封裝是在實體屬性前加上兩個底線,這樣存取或設定實體屬性就得透過 getter 或 setter 方法。
- 若將 __init__() 方法其中的參數設定為 None ,便可依條件判斷來設定實體屬性值。
- 關鍵字 is 用來判斷兩個物件是否相等。
問題與討論
- 類別中自訂 __init__() 方法有什麼優點?
- 為什麼 Python 的屬性要預設為可以公開存取?
- 要怎麼替方法建立不同的參數版本?
練習
- 寫一個程式 exercise1101.py ,替 exercise1001.py 的 IntegerDemo 設計 __init__() 方法。
- 寫一個程式 exercise1102.py ,替 exercise1002.py 計算階層值的類別設計 __init__() 方法。
- 寫一個程式 exercise1103.py ,替 exercise1003.py 計算費氏數列的類別設計 __init__() 方法。
the end
沒有留言:
張貼留言