複製與參照 <<
Previous Next >> 類型註解
物件導向
請學員瞭解關於物件導向的概念及特性。
非物件導向
名詞解釋:
- 變數 (Variable):代表儲存一個或多個的單一資料或資料集,能夠被定義 (Define) 與引用 (Reference)。
- 函式 (Function):封裝的程式碼(在程式中定義),以能在特定需求時輸入資料與輸出結果。
通常物件導向的程式語言會比較高階,也能夠支援非物件導向的操作。不過有些特例如 C# 語言,所有程式碼必須在物件導向概念中實現,非物件導向的實作則是「靜態函式」對應一般函式;「靜態類別」對應模組 (Module),甚至連進入點 (Entry point) 都必須在類別中。
物件導向
名詞解釋:
- 類別 / 類型 / 型別 (Type / Class / Identifier):定義資料形式與資料操作。類別 (Type) 一般是指簡單二進位可表達的資料型態;類型 (Class) 是指特別定義過的資料結構或資料集;型別 (Identifier) 則是強型別 (Strong typed) 程式語言用來標記資料格式的名稱。
- 物件 (Object):是類別的實例 (Instance),可以被變數儲存。
在擁有物件導向的程式語言中,函式也是一種物件,並且有自己的類別,如匿名函式 (Anonymous function, Function literal, Lambda expression)。
程式中若要獲得物件,是由類別產生,或使用字面表示式 (Literal expression)。而某些能夠支援更抽象概念的程式語言中,類別也是一種物件,因此類別也有自己的類別,稱為元類 (Meta Class),如 Python。
類別有以下特性:
- 封裝性 (Encapsulation):可以攜帶各種資料或資料集,並且由於物件是實例的關係,每個物件是獨立個體,其資料可以有著不同的內容。
- 繼承 (Inheritance):定義簡單的類別後,較複雜的類別可以從簡單的類別提取其功能。
- 多形 (Polymorphism):不同類別可能會有相同操作。程式碼在使用該物件時,強型別的程式語言(如 C++)必須特別規範,弱型別的程式語言(如 Python)會嘗試尋找並進行該操作。
- 抽象性 (Abstraction):透過繼承和元類概念可以表達更抽象的物件概念。
以下將示範 Python 程式碼中如何用封裝性與繼承呈現各種特性。
封裝性
Python 的基本類型定義語法如下:
# Python 中的類型名稱使用 Camal case(單字首大寫)。
# 每個類型與函式定義之間空兩行。
class MyClass:
"""定義 my class。"""
def __init__(self, score):
"""初始化函式。
類別中的 function 稱為方法 (Method),
其中第一項參數 self 代表此物件。
實際呼叫時會自動填入。
"""
print("初始化!")
# 類別中的「變數」稱為成員 (Member)。
# 類別中的「名稱」稱為屬性 (Attribute)。
# members 和 methods 都是 attributes。
# attributes 使用「點」運算子取得。
self.score = score
# 私有 attributes 名稱前加上雙底線。
# 名稱會被編碼為 "_MyClass__score"。
# 除非用上述名稱呼叫,
# 只有本類別的 method 可以呼叫。
self.__score = score + 10
def method1(self, p0):
"""一個公開的 method,
任何使用本物件的程式都可以呼叫。
"""
print("method 1:", self.__score)
# 呼叫一個私有 method。
self.__method2(p0)
def __method2(self, p0):
"""一個私有的 method。"""
print("method 2:", p0)
# 初始化一個 MyClass 實例。
a = MyClass(50) # 初始化!
print(a.score) # 50
a.method1(20) # method 1: 60
# method 2: 20
# 強迫呼叫
print(a._MyClass__score) # 60
# 強迫呼叫
a._MyClass__method2(30) # method 2: 30
上面的類型中,呈現了封裝性的效果,一個 MyClass 物件中擁有公有與私有的 attributes,當使用 a 名稱傳送此物件時,隨時可以使用公開 attributes。
繼承
繼承可以將較小類型的 attributes 全部拿來用。在 Python 中,有個類型叫做 object,提供一些預設特性,所有類型如果沒指定都會繼承它。
繼承語法如下:
# 引用斜邊公式 hypotenuse。
from math import hypot
class Point:
"""點資料集。"""
def __init__(self, x, y):
"""初始化時設定兩座標並顯示。"""
self.x = x
self.y = y
self.__show()
def __show(self):
"""印出 x 和 y。"""
print(self.x, self.y)
def distance(self, c):
"""回傳兩點的距離。"""
return hypot(c.x - self.x, c.y - self.y)
def move(self, x, y):
"""移動座標。"""
self.x = x
self.y = y
class PointView(Point):
"""檢視用的類型,繼承自點。"""
def __init__(self, x, y):
# 引用上一個繼承項的 __init__ 函式。
super(Coordinate, self).__init__(x, y)
print("Point view")
def distance_with_origin(self):
"""此點與原點的距離。"""
return hypot(self.x, self.y)
def __repr__(self):
"""覆寫預設函式 __repr__。
此函式可以定義物件轉成字串時的樣式。
"""
return f"<Point x={self.x} y={self.y}>"
# 建立點資料 p1。
p1 = Point(20, 30) # 20 30
# 建立點資料 p2。
p2 = PointView(50, 70) # 50 70
# Point view
# 顯示距離。
print(p2.distance(p1)) # 50
# 顯示 p2 與原點的距離。
print(p2.distance_with_origin()) # 86.02325267042627
# 印出 p1 和 p2。
# 印的時候會呼叫預設函式 __repr__。
print(p1) # <Point object at 一段記憶體位址>
print(p2) # <Point x=50 y=70>
上面的範例中,PointView 繼承自 Point,因此會擁有所有 Point 的公有與私有 attributes,但是私有 attributes 會被編成 _Point__show 的樣式,因此不能直接使用。
另外,當 PointView 的屬性名稱與 Point 重複時(如 __init__),會直接覆蓋,因此必須使用 super 函式搜尋到 Point 類型,將 PointView 實體帶入 Point.__init__ 執行。
靜態方法
靜態方法 (static method) 只是掛上類別名稱的函式 (static method)。在 Python 中,靜態方法可以有兩種定義法,稱為 static method 與 class method。
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, p):
"""實做相等運算子 ==。"""
return self.x == p.x and self.y == p.y
@staticmethod
def s_method(x, y):
"""Static method."""
return Point(x + 10, y + 10)
@classmethod
def c_method(cls, x, y):
return cls(x + 10, y + 10)
p1 = Point.s_method(20, 30)
p2 = Point.c_method(20, 30)
print(p1 == p2) # True
兩者的差異只在於 class method 的會傳入當前類型,所以在繼承上有差別。
延伸閱讀 - 類別與實體的關係
類別也有 attributes,稱為 class attributes。如 Point.__init__、Point._Point__show 等。而 p1.x 是因為上面的範例使用 self.x = x 的語法產生,這是後天賦予物件的,又稱 object attributes。物件可以使用 class attributes,但是類別沒有 object attributes。
關於 class attributes,每個 object 的 class member 其實是使用相同名稱,但是 class method 是複製過來的,如下:
class A:
a = 10
def method(self, p0, p1):
pass
a = A()
b = A()
print(a.a is b.a) # True
print(a.method is b.method) # False
是因為下列程式是相同結果的:
a.method(p0, p1)
A.method(a, p0, p1)
Python 內建的 type 函式會回傳實體的類型。而事實上 type 是一種類型,而且是所有類型的元類,因此初始化時可以「作出」實體的類型。
複製與參照 <<
Previous Next >> 類型註解