之前說到OO程式設計的實際運作,就是將資料(attributes)和使用這些資料的方法(methods)「封裝」在類別(class)中。今天起我們將逐漸「接地氣」,一步步依次介紹Python如何實作OO的三大支柱封裝、繼承和多型。
class Tree(): # class
def __init__(self, breed, age): # constructor(建構子/建構式/建構函數/建構方法)
self.breed = breed
self.age = age
class
是Python的保留字,目的就是定義類別。class
保留字來定義一個名為Tree的類別。注意:Tree的T
是大寫。這雖不是語法上的強制,卻是PEP 8的命名規範(註1)。強烈建議乖乖遵從。self.breed
和self.age
。__init__()
。這是個特別的方法,OO術語稱為constructor,中文可譯作建構子、建構式、建構函數、建構方法等。筆者通常用建構子,更多時候直接用英文constructor。In class-based object-oriented programming, a constructor (abbreviation: ctor) is a special type of subroutine called to create an object. It prepares the new object for use, often accepting arguments that the constructor uses to set required member variables. (摘自wiki)
上面這段英文大意是:constructor是個物件建立時(自動)執行的特殊方法。功能通常是設定該物件屬性的初值,及/或初始化其他資源。
以筆者學過的幾種OO程式語言(可惜號稱最純種的OO語言Smalltalk
筆者至今無緣一親芳澤),都有constructor機制。C++除了constructor外,還有destructor。
不同語言,constructor在語法層面自然有所不同。例如C-like這掛語言大概都以「和類別同名且無傳回值」的方法作為constructor。
Python的constructor和C-like語言很不一樣,用的是__init__()
方法。
本例中的constructor,任務是將傳入的兩個參數breed
和age
賦予給類別的屬性breed
和age
(即self.breed
和self.age
)。請分清楚何者是傳入參數,何者是類別屬性。
傳入的第一個參數self
另有用途,以後有專文討論。
一般而言,constructor是在物件建立時,由系統自動執行,而不是由寫程式者呼叫(或稱調用)。
不過Python的constructor__init__()
卻允許由使用者手動呼叫(有些OO語言不可以),呼叫方式和一般物件.方法
相同,請參考下面的code。
class Tree(): # class
# constructor。當然啦,要允許建立物件時「不傳參數」,那麼這些參數都得有「預設值」才行。
def __init__(self, breed='', age=-1):
self.breed = breed
self.age = age
tree1 = Tree('cedar', 1200) # 正常做法是在建立物件時就將參數傳給constructor。
print(f'\n.建立物件時傳遞參數給建構子: {tree1.breed=}, {tree1.age=}\n')
tree2 = Tree()
print(f'.建立時沒傳參數給建構子: {tree2.breed=}, {tree2.age=}')
tree2.__init__('Oak', 580) # Python允許外部呼叫constructor。
print(f'.物件建立後才呼叫建構子並傳參數: {tree2.breed=}, {tree2.age=}')
輸出如下:
對Python的constructor,「建立物件時系統自動呼叫」及「事後自己外顯呼叫」兩者之間,在功能和效能上是否存在差異,還有待研究。如果事後外顯呼叫沒有明顯優勢,筆者強烈建議讓系統自動幹活,沒必要自己弄髒手。
註1:程式語言中,各種multiple-word identifiers(多字組成的識別符),如變數、常數、函數、類別...等等的名稱,大致有以下幾種「命名慣例」(naming conventions)。略舉耳,未窮盡:
FAMILY_NAME
, USER_LOGIN_ID
, REST_API_WEB_APP
。這種方式普遍用於常數(constant)。不過Python並無真正的常數,在Python用全大寫來表示常數,只是community間的默契(這回換個詞兒,不說慣例了)。FamilyName
, UserLoginId
, RestApiWebApp
。familyName
, userLoginId
, restApiWebApp
。_
隔開。例如:family_name
, user_login_id
, rest_api_web_app
。PEP 8建議變數和函數用Snake Case,類別則用Pascal Case。