tutor
python
本文所有內容與資料皆由本人蒐集與撰寫,轉載請註明出處。
程式語言分為高階語言、組合語言、機器語言等等,Python屬於高階語言的一種。機器語言與組合語言直接控制電腦硬體,但難以閱讀與開發;高階語言易於閱讀與開發,但需要「翻譯」給電腦聽。
來自 Python 官方網站的介紹:
Python 是一種易學、功能強大的程式語言。它有高效能的高階資料結構,也有簡單但有效的方法去實現物件導向程式設計。Python 優雅的語法和動態型別,結合其直譯特性,使它成為眾多領域和大多數平臺上,撰寫腳本和快速開發應用程式的理想語言。
常見應用:網站開發、資料分析、機器學習、遊戲開發、網路爬蟲…
其他語言:C、C++、R、Java、JavaScript、SQL、Go、Ruby…
以臺大資工系必修為例:
以臺大資管系必修為例:
>>> 1 + 2
3
for i in range(3):
print(i)
print("The loop ends.")
Output:
0
1
2
The loop ends.
print("Hello World!")
print()
是一個函數 (function)>>> # This is a comment(註解)
>>> # A comment will not be executed by python
>>> 1 + 2
3
>>> 3 - 1
2
>>> 5 * 2
10
>>> 5 ** 2 # 5 的 2 次方
25
>>> 8 / 5 # 8 除以 5(回傳小數)
1.6
>>> 8 // 5 # 8 除以 5 的商
1
>>> 8 % 5 # 8 除以 5 的餘數(取 mod)
3
>>> (50 - 5 * 6) / 4
5.0
=
可以將數值指派給變數,可以參考 基本命名規則a = 10
意為指派 10 給 a(右邊的值丟給左邊的容器)a == 10
意為比較 a 是否等於 10(為邏輯判斷式)>>> width = 20
>>> height = 5 * 9
>>> width * height
900
>>> var = input()
3 # 使用者自行輸入
>>> print(var)
3 # 電腦將 var 的值印出
int a = 1
3
3.0
'a'
"This is a string"
True
(Non-zero) / False
(Zero)補充:String 是由 Character 組成的陣列,其他語言有可能會將 String 與 Character 當作兩種資料類別,但在 Python 中沒有 Character 的概念,因此長度為一的字母在 Python 中也會被當成字串來做處理。
>>> str(3)
'3'
>>> int("3")
3
>>> float(3)
3.0
>>> float("string")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: could not convert string to float: 'string'
>>> type(3) # 檢查資料類別
<class 'int'>
a = 10
print(a)
a = a + 2 # 把 a + 2 指派給 a
print(a)
a += 2 # a 自己等於自己 + 2
print(a)
Output:
10
12
14
相同的還有
-=
/=
*=
//=
**=
%=
…
and
:且or
:或not
:非非常簡易版的架構圖如下:
補充 - GPU(顯示卡):負責遊戲、3D繪圖、機器學習等等運算
若對這個有興趣的話,可以去查「計算機結構」或修相關課程
若對這個有興趣的話,可以去查「數位邏輯」、「電路學」或修相關課程
一些排版準則如下:
寫程式除了讓他可以執行以外,讓別人看懂也是一件很重要的事情。
當未來需要進行多人的大型開發時,程式碼的簡潔易懂可以大大加快開發協作時間。
想了解更多可以搜尋 Google coding style 或 SOLID 原則。
price = int(input())
if price < 100:
print("It's cheap.")
elif price >= 100 and price < 200:
print("It's okay.")
else:
print("It's too expensive!")
if
) ,否則若…則… (elif
) ,若以上皆非則… (else
)if
跟 else
是一組的,後面要放條件判斷 or 布林值,elif
可有可無if-else
(巢狀條件判斷)
if ...
if ...
...
else
...
else ...
if ...
...
else
...
if
condition else
B(若 condition 為真,則執行 A ,否則執行 B)while
跟 for
while
可以想成不斷執行的 if
,直到條件不再成立為止
for
則是針對清單內的元素,每個都執行一次所有指令
i = 0
while i < 3:
print(i)
i += 1
print("The loop ends.")
for i in [0,1,2]: # Or for i in range(3):
print(i)
print("The loop ends.")
Output:
0
1
2
The loop ends.
備註:
i
is called 'Loop Counter' in above examples
For 迴圈會自動更新 Loop Counter,While 迴圈則不會
while 4 > 3: # A always true condidtion
print('Loop')
上面的例子中有使用到 range(),而 range() 是能夠幫助我們創造一個範圍的函數,其用法為:
range(n)
會回傳 [0,1,2,...n-1]
的清單range(m,n)
會回傳 [m,m+1,m+2,...n-1]
的清單range(m,n,k)
會回傳 [m,m+k,m+2k,...]
的清單,最後一個元素不超過 n-1一般的情況下使用第一個就好。
備註:回傳型態其實不完全是清單,但我們先把它當成清單用就好
可以透過list()
將其轉為清單
for i in range(3):
print(i)
for j in range(2):
print(">",j)
print('=====')
Output:
0
> 0
> 1
=====
1
> 0
> 1
=====
2
> 0
> 1
=====
break
:跳出迴圈外,直接結束迴圈執行continue
:跳過後面的指令,直接結束此次迴圈,並進行下一次迴圈break
& continue
!
i = 0
while (i < 10):
i += 1
if i == 2:
continue
if i == 6:
break
print(i)
Output:
1
3
4
5
>>> s = 'String'
>>> print(type(s))
<class 'str'>
>>> print(s[0])
'S'
>>> print(s[-1])
'g'
>>> print(len(s))
6
>>> print(s+s)
'StringString'
>>> print(s,s)
String String
>>> print(s+"&"+s)
String&String
>>> print(s*3)
StringStringString
>>> print(s.replace('Str','do'))
doing
>>> print(s.find('ing'))
3
>>> print(s.upper())
STRING
>>> print(s.lower())
string
>>> print('t' in s)
True
>>> l = [1, 1.0, 10, "test"]
>>> print(l[0])
1
>>> print(l[2])
10
>>> l[2] = 100
>>> print(l[2])
100
>>> print(l[-1])
"test"
>>> print(len(l))
4
>>> l.append("123")
>>> print(l)
[1, 1.0, 100, "test", "123"]
>>> l.pop()
"123"
>>> print(l)
[1, 1.0, 100, "test"]
>>> print(l + l)
[1, 1.0, 100, "test", 1, 1.0, 100, "test"]
>>> print(l * 3)
[1, 1.0, 100, "test", 1, 1.0, 100, "test", 1, 1.0, 100, "test"]
l = [1,2,3,4,5]
for i in l: # or for i in range(len(l))
print(i * 2)
Output:
2
4
6
8
10
Method | Usage |
---|---|
list.append(x) | Add element x to end of list. |
list.sort() | Sort (order) the list. A comparison function may be passed as a parameter. |
list.reverse() | Reverse the list. |
list.index(x) | Returns index of first occurrence of x. |
list.insert(i, x) | Insert x into list at index i. |
list.count(x) | Returns the number of occurrences of x in list. |
list.remove(x) | Deletes the first occurrence of x in list. |
list.pop(i) | Deletes the ith element of the list and returns its value. |
# Wrong Copy (Reference Copy Only)
aList = [1, 2, 3]
anotherList = aList
anotherList[0] = 5
print(aList)
# Check their address
print(id(aList), id(anotherList))
Output:
[5, 2, 3]
1805364504896 1805364504896
anotherList
時,原本的 aList
也一同被修改有以下幾種方式可以正確地複製 List:
# Correct Copy
aList = [1, 2, 3]
# Three different ways to copy a list (Shallow)
anotherList = list(aList)
anotherList = aList[:]
anotherList = aList.copy()
anotherList[0] = 5
print(aList)
# Check their address
print(id(aList), id(anotherList))
Output:
[1, 2, 3]
1805364505024 1805364643392
補充:此處使用的稱為「Shallow Copy」,僅複製容器中元素的地址
若連容器中的元素本身都想完全複製,需要使用「Deep Copy」
延伸閱讀: Python - 淺複製(shallow copy)與深複製(deep copy)
QuotaAmount,StartDate,OwnerName,Username
150000,2016-01-01,Chris Riley,trailhead9.ub20k5i9t8ou@example.com
150000,2016-02-01,Chris Riley,trailhead9.ub20k5i9t8ou@example.com
150000,2016-03-01,Chris Riley,trailhead9.ub20k5i9t8ou@example.com
150000,2016-01-01,Harold Campbell,trailhead14.jibpbwvuy67t@example.com
150000,2016-02-01,Harold Campbell,trailhead14.jibpbwvuy67t@example.com
150000,2016-03-01,Harold Campbell,trailhead14.jibpbwvuy67t@example.com
150000,2016-01-01,Jessica Nichols,trailhead19.d1fxj2goytkp@example.com
150000,2016-02-01,Jessica Nichols,trailhead19.d1fxj2goytkp@example.com
150000,2016-03-01,Jessica Nichols,trailhead19.d1fxj2goytkp@example.com
結合前面的字串與清單處理方式,我們可以輕鬆的處理 CSV file 中的每一行資料:
>>> line = 'amount,date,owner,user'
>>> data = line.split(',')
>>> print(data)
['amount', 'date', 'owner', 'user']
>>> print(data[0])
amount
那如何處理整個 CSV file?使用檔案處理相關函數(之後再講):
result = []
with open('file.csv') as f:
data = f.read()
lines = data.split('\n')
for line in lines:
result.append(line.split(','))
print(result)
Output:
[[QuotaAmount,StartDate,OwnerName,Username],
[150000,2016-01-01,Chris Riley,trailhead9.ub20k5i9t8ou@example.com],
[150000,2016-02-01,Chris Riley,trailhead9.ub20k5i9t8ou@example.com],
...]
我們以前寫 print('Hello') 時,其實就是在呼叫函數,這個函數會幫我們把我們傳入的 'Hello' 印出來。其他像是 range()、type()、input() 等也都是函數,各有不同的用途。
我們也可以透過特定語法來定義自己的函數,透過函數可以幫我們達成「模組化」,省去重複的 code 同時提供更多彈性來執行類似的動作。
一個函數包含名稱、本體、輸入(Input)與輸出(Output),後兩者又叫做參數(Parameters)與回傳值(Return Values)。有時我們也會在函數最一開始的地方加入註解,來說明函數的使用方式以及參數 / 回傳值類型。
以下圖為例,輸入是蘋果,輸出是切半的蘋果,函數 h
的作用是把蘋果切半。
def
來宣告函數,名稱接在 def
後面return
來控制函數的結束點,並將回傳值放在後面return
則會自動在最後加上 return None
以下是一個在 python 中的實際例子,輸入是一個數字 n
,輸出是一個清單 alist
,函數 get_1_to_n
的作用是獲取 1 ~ n 的清單。
def get_1_to_n(n = 5):
print('Getting a list range from 1 to',n)
alist = list(range(1,n+1))
return alist
x = get_1_to_n(10) # or get_1_to_n(n = 10)
print('X = ',x)
print('~~~~~~~~~~')
y = get_1_to_n()
print('Y = ',y)
Output:
Getting a list range from 1 to 10
X = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
~~~~~~~~~~
Getting a list range from 1 to 5
Y = [1, 2, 3, 4, 5]
變數依據生命週期的不同,分為全域變數與區域變數。
return var
scale = 3 # Global
def multiply(num):
return num * scale
def multiply_5(num):
scale = 5 # Local
return num * scale
print(scale)
print(multiply(2))
print(multiply_5(2))
print(scale)
Output:
3
6
10
3
補充:在函數內修改全域變數與上一層變數的方法:Global & Nonlocal
在 Python 中,不同資料類別又可以其性質分為可變物件與不可變物件。
分類 | 可變物件 | 不可變物件 |
---|---|---|
說明 | 被創造出來後,其值可以被改變的物件。 | 被創造出來後,其值無法被改變的物件。 |
舉例 | list, dict, set* | int, float, string, tuple |
修改 | 可以,依資料類別不同有不同修改方式,修改時記憶體位置不會改變。 | 無法,只能透過重新指派的方式,此時記憶體位置亦會被重新分配。 |
# Mutable Object
alist = [1,2,3]
alist = [4,5,6] # Okay
alist[1] = 100 # Okay
# Immutable Object
astring = 'string'
astring = 'STRING' # okay
astring[1] = 'A' # TypeError: 'str' object does not support item assignment
接著我們來看看記憶體位址的變化:
# Let's take a look on the addresses of these objects
# id() is a function help finding address of a variable
# Mutable Object
alist = [1,2,3]
print(id(alist))
alist[1] = 100
print(id(alist))
print('=====')
# Immutable Object
astring = 'string'
print(id(astring))
astring = 'STRING'
print(id(astring))
Output:
1541330859072
1541330859072
=====
1541255790064
1541259351920
參考 什麼是 Immutable & Mutable objects
*關於 set 可不可變其實有點爭議,在這裡先當作他是可變的
此處我們「不會」深入講當傳參數時發生了什麼事情,因為牽扯到一些記憶體跟參照等等的概念,我們會用幾個例子來說明何謂 Python 的 Pass by Assignment。
Python 中函數依據傳入參數的類別不同,會有不同的行為。
聽起來很複雜對吧?我們直接用例子來看會比較清楚一些:
def listchange(l):
l[0] = 'A'
alist = [1,2,3]
listchange(alist)
print(alist)
Output:
['A',2,3]
def strchange(s):
s = 'STRING'
astring = 'string'
strchange(astring)
print(astring)
Output:
string
在以上的例子中,alist
為可變物件,因此做為參數傳入並在函數中修改時,原始的 alist
也一同被修改;而 astring
為不可變物件,因此做為參數傳入時,我們並無法直接修改他的值,只能透過重新指派的方式給予 'STRING'
這個值,而原始的 astring
依然存放 string
這個值沒有改變。
我們在撰寫函數時,比較好的方式是不要直接修改原始參數的值,而是將修改後的值存放在新的變數中,並作為回傳值傳回給呼叫函數的地方,以避免混淆的狀況。
遞迴是一種概念,指的是「在函數在執行過程中呼叫自己」的方法。這種技術對於解決某些複雜問題特別有用,例如處理樹狀結構、遞迴搜尋、組合數學等。以下是遞迴的基本概念和特性:
def find_fact(n):
if n == 1: # base case
return 1
else: # recursive case
recurse = find_fact(n-1)
result = n * recurse
return result
補充:遞迴深度的上限
關於函數還有很多可以講:Recursion 的設計方法、Call by Reference、Call by Value…。但有些東西太進階了,我們先停在這裡,以後有機會或是遇到的時候再細講。
[]
,tuple 使用 ()
(item, )
表示>>> mytuple = (11, 22, 33)
>>> saved = mytuple
>>> mytuple += (44,)
>>> mytuple
(11, 22, 33, 44)
>>> saved
(11, 22, 33)
num = [1,2,3]
char = ['a','b','c']
CHAR = ['A','B','C']
for i in zip(num, char, CHAR):
print(i)
Output:
(1, 'a', 'A')
(2, 'b', 'B')
(3, 'c', 'C')
當我們需要了解某個字的讀音時,我們會去查找字典,在其中尋找對應的讀音。這種 {字: 讀音} 的配對,在 Python 中可以透過字典來實現。
collections
這個 library 中有提供有序字典)in
或 dict.get()
來檢查
mydict = dict() # or mydict = {}
print("Here is an empty dictionary:", mydict)
# Add new pair in dictionary
mydict[1] = 'one'
mydict[2] = 'two'
mydict[3] = 'three'
print("Dictionary now looks like: ", mydict)
print("2 is corresponding to:", mydict[2]) # Access value
Output:
Here is an empty dictionary: {}
Dictionary now looks like: {1: 'one', 2: 'two', 3: 'three'}
2 is corresponding to: two
# Continued from last cell
print(mydict.keys())
print(mydict.values())
print(mydict.items())
print(5 in mydict)
print(mydict.get(5))
Output:
dict_keys([1, 2, 3])
dict_values(['one', 'two', 'three'])
dict_items([(1, 'one'), (2, 'two'), (3, 'three')])
False
None
set[0]
的方式來取值set.add(item)
與 set.remove(item)
來新增與刪除元素set(list)
將 List 轉為 Set,藉此檢查清單中唯一元素個數
aset = {11, 22, 33}
bset = {33, 44, 55}
print("Union:", aset | bset)
print("Intersection:", aset & bset)
print("Difference(A-B):", aset - bset)
print("Difference(B-A):", bset - aset)
print("Symmetric difference:", aset ^ bset)
Output:
Union: {33, 22, 55, 11, 44}
Intersection: {33}
Difference(A-B): {11, 22}
Difference(B-A): {44, 55}
Symmetric difference: {11, 44, 22, 55}
補充:特別注意 運算元優先順序!
類別 | Tuple | List | Dict | Set |
---|---|---|---|---|
符號 | ( ) | [ ] | { } | { } |
可變性 | 不可變 | 可變 | 可變 | 可變 |
順序性 | 有序 | 有序 | 無序 | 無序 |
在 Python 中,很常會用到檔案相關的操作,舉凡文字檔(.txt)、CSV檔(.csv)、圖片檔(.png, .jpg…)、影片檔(.mp4, .avi…)等等,都會需要讀取、寫入檔案。這邊先以文字檔作為示範,僅簡單講解基礎操作,其他檔案如圖片、影像有些會有專門的 library 來處理。
test.txt:
This is a test txt file.
This is another line.
全部讀入
file = open('test.txt','r')
data = file.read()
file.close()
print(data)
data = data.split('\n')
print(data)
Output:
This is a test txt file.
This is another line.
['This is a test txt file.', 'This is another line.']
逐行讀入
file = open('test.txt','r')
while True:
line = file.readline()
if not line:
break
print(line,end='')
file.close()
Output:
This is a test txt file.
This is another line.
寫入檔案
file = open('test.txt','a')
file.write('This is a new line.')
file.close()
test.txt:
This is a test txt file.
This is another line.
This is a new line.
file = open('test.txt','w')
file.write('This is a new line.')
file.close()
test.txt:
This is a new line.
要記得加上 file.close()
來關閉檔案,以免造成潛在的 Memory Leak。有一個更好的寫法,使用 with
來達成,如下例:
with open('test.txt','r') as file:
# Do some file-related operations
# The file will close automatically when this block is finished
# Do other operations
將檔案相關操作放在 with
的區塊中,而非檔案相關操作放在外面,一方面能確保檔案有被關閉,一方面也能增加可讀性。
寫程式難免會遇到 Error 的狀況,當我們不希望程式因為 Error 而停止執行並噴出一大堆錯誤訊息時,可以使用以下技巧來處理。
try
必須搭配 except
,預設先執行 try
裡的程式碼try
執行失敗時, except
裡才會被執行if...else...
有異曲同工之妙,但不會因遇到錯誤而停止執行raise
來定義自己想要的 Exception
x = input()
if not x.isdigit():
raise Exception("Not A Integer")
else:
x = int(x)
try:
inv = 1/x
print(inv)
except ZeroDivisionError:
print('Denominator cannot be 0!')
except TypeError:
print('Type is not correct!')
except:
print('Other Error!')
Output (when input = 2): 0.5
Output (when input = 0): Denominator cannot be 0!
Output (when input = 'A'): ... Exception: Not A Integer
此處要注意的是,前兩種狀況程式可以順利結束,因為我們使用 except
來處理分母為0的例外;但第三種狀況 Python 會報錯,程式中斷無法繼續往下執行,因為我們 寫「當 x 不是數字時就 raise error」,Python raise error 後就會停止。
Assertion 提供一種保護機制,確保執行到某個地方時,某樣我們預期的條件仍然成立。若不成立則會丟出 Assertion Error,可以加入自定義的訊息。
x = int(input())
assert x >= 0, 'x is not positive'
print('A Positive number is:',x)
Output (when input = 1): A Positive number is: 1
Output (when input = -1): ... AssertionError: x is not positive
Lambda 又叫做匿名函數,當我們需要快速簡潔的撰寫一個函數,但又不想幫他命名時(意即這個函數可能只會用一兩次),我們就會使用 Lambda 來幫助我們。Lambda 在使用上依然可以給予名稱,但非必要,函數內容也必須在一行內結束。
>>> print((lambda x : x ** 2)(10))
100
>>> print((lambda x, y: x * y)(4, 5))
20
>>> print((lambda x: x[1])([1,2,3]))
2
numbers = [1,10,100,1000,10000]
bignums = filter((lambda x: x > 50), numbers)
print(list(bignums))
Output:
[100,1000,10000]
numbers = [1,10,100,1000,10000]
doublenums = map((lambda x: x * 2), numbers)
print(list(doublenums))
Output:
[2, 20, 200, 2000, 20000]
food = [('Apple',10),('Coke',30),('Bread',5),('Candy',25)]
food_sorted = sorted(food, key = lambda x: x[1])
print(food_sorted)
Output:
[('Bread', 5), ('Apple', 10), ('Candy', 25), ('Coke', 30)]
Python 強大的地方就是其眾多的使用者,讓我們在網路上有許多參考資料,以及眾多的第三方套件可供我們使用。套件其實就是別人寫好的 .py 檔案,將其整理後丟到網路上,讓我們可以透過一行簡單的 import [package]
就能使用。
pip install [package]
os
, random
, time
等等已預先包含在 python 中,就不需再另外下載import [package]
(推薦)from [package] import *
from [package] import [module/function]
(推薦)import [package].[module]
import [package] as [name]
[package].[function]
的方式,若是僅載入特定模組/函數的話,前面不須加套件名稱即可使用。以下四種方式結果皆相同,但為了方便管理我們一般會選用第一種,以便知道各個函數來自哪個套件,同時也不會不小心覆寫(Overwrite)某些函數。
import os
print(os.getcwd())
from os import getcwd
print(getcwd())
import os as O
print(O.getcwd())
from os import getcwd as gw
print(gw())
進階:參考 Python 的 import 陷阱
參考 Python 第三方模組
Git 是一種版本控制系統,它可以追蹤軟體開發過程中的變更,幫助開發人員更有效地管理程式碼。使用 Git 有許多好處:
Git 在許多著名的開源軟體專案中得到廣泛使用,包括Linux核心、Ruby on Rails 和 jQuery。使用 Git 的方式一般有兩種:使用指令(Command Line)或是下載 Github Desktop 使用其軟體介面(GUI),我們這邊會先介紹如何使用 Github Desktop。
Credit: The world-wide famous ChatGPT
參考以下連結:
物件導向程式設計是軟體設計的一種方法,它把軟體分成數個「物件」來撰寫。每個物件都有自己的屬性和行為,並且可以跟其他物件互動。這樣的好處是,軟體的各部分之間彼此獨立,不但便於重複使用,也更容易理解和修改,提高軟體的可維護性和可擴展性。
物件導向程式設計是目前最流行的軟體設計方法之一,被廣泛應用於各種領域,包括網站開發、商用軟體、遊戲開發等等。常見的物件導向程式設計語言包括 Java、C++、C#、Python 等。
Credit: The world-wide famous ChatGPT
以下我們使用一個簡單的例子來說明類別的概念:在現實生活中,有各式各樣的車子,而每台車子雖然皆不相同,但都具有共同特徵,像是有四個輪胎、都有駕駛與車牌跟廠牌、都使用汽油前進等等,這時候就很適合使用物件導向的概念為車子建造一個類別。
在以下的例子中,Car
是一個類別名稱,這個類別包含 driver, engine, meter
等等屬性(Attribute),以及 turnOnEngine, checkEngine, drive
等等方法(Method)。
而 mycar
是一個屬於 Car
類別的物件或變數,我們也可以建立多個屬於 Car
類別的物件像是 mycar1, mycar2...
,彼此之間的屬性與函數操作互不影響。
在宣告類別時,我們使用以下語法:
class Car
代表這個類別的名稱,亦可使用 class Car()
或 class Car(object)
def __init__()
是一種特殊的類別方法,也稱為建構函式
__init__()
,建立此類別物件時會自動執行,不須呼叫
class Car: # or class Car(): / class Car(object):
def __init__(self, plateID, driver):
self.wheels = 4
self.plateID = plateID
self.driver = driver
self.engine = False
self.meters = 0
self.turnOnEngine()
補充:在其他語言(如 C++)中,時常會見到解構函式(Destructor)的使用
與建構函式相對應,解構函式在物件被銷毀時會自動執行
其使用主要是為了刪除 Runtime 時動態分配的記憶體空間,以避免 Memory Leak
Python 中也有提供解構函式,但因為我們通常不會自己分配記憶體
所以大多狀況下不需要使用,Python 會自己幫我們刪除分配的空間
list
、int
、string
等等,甚至可以是另一個類別
class Car: # or class Car(): / class Car(object):
def __init__(self, plateID, driver):
self.wheels = 4
self.plateID = plateID
self.driver = driver
self.engine = False
self.meters = 0
self.turnOnEngine()
def turnOnEngine(self):
if self.checkEngine():
self.engine = True
print("Engine Started!")
def checkEngine(self):
return True
def drive(self, distance):
if self.engine:
self.meters += distance
print("Drive %d kilometers."%distance)
else:
print('Engine is not turned on.')
def turnOffEngine(self):
self.engine = False
print("Engine has been turned off.")
def whoisDriving(self):
print('%s is driving the car.'%self.driver)
return self.driver
def getMeters(self):
return self.meters
你應該有注意到上面出現了很多個 self
這個關鍵字,這個關鍵字在類別中扮演了很重要的角色,且任何類別方法中,第一個參數一定要是 self
,他的意思是「這個物件本身」,而因為 self
代表這個方法中的物件本身,所以這個位置不需要傳入任何東西,在呼叫時可以直接無視。
用以下的例子來說:
class Car:
...
def drive(self, distance):
if self.engine:
self.meters += distance
print("Drive %d kilometers."%distance)
else:
print('Engine is not turned on.')
def getMeters(self):
return self.meters
...
print(myCar1.getMeters())
myCar1.drive(100)
print(myCar1.getMeters())
print(myCar2.getMeters())
print(myCar3.getMeters())
Output:
100
200
1000
0
可以注意到幾個重點:
self.meters
都不同mycar1.getMeters()
時,不用傳入任何參數,但定義時卻必須定義一個參數 self
,也就是說,類別方法的傳入參數量 + 1 = 定義參數量self.turnOnEngine()
上述例子中,有些屬性是屬於整個類別共享的,像是 self.wheels
,所有車子都有四個輪子。此時我們可以利用靜態變數,來宣告整個類別的屬性。詳細作法如下:
class Car: # or class Car(): / class Car(object):
wheels = 4
def __init__(self, plateID, driver):
self.plateID = plateID
self.driver = driver
self.engine = False
self.meters = 0
self.turnOnEngine()
...
變數/屬性 | 說明 | 例子 | 語法 |
---|---|---|---|
實體變數 | 每個物件的屬性 | 每輛車有不同的駕駛 | mycar.driver |
類別變數 | 整個類別的屬性 | 所有車都有 4 個輪子 | Car.wheels |
在完成以上的宣告後,接著我們來看使用方式:
Car()
來建立一個類別物件myCar.drive()
來呼叫類別方法myCar.driver
來獲取類別屬性
myCar = Car("ABC-0311","Jack")
print('=====')
myCar.drive(100)
print('=====')
driverName = myCar.driver
print(driverName, "is driving the car.")
Output:
Engine Started!
=====
Drive 100 kilometers.
=====
Jack is driving the car.
=====
補充:在其他語言中,為了更好的管理獲取權限
有時候會限制類別屬性或方法的取的與使用
此舉可以避免類別屬性被意外的修改,像 C++ 中private
與protected
的使用
class Car: # or class Car(): / class Car(object):
wheels = 4
def __init__(self, plateID, driver):
self.plateID = plateID
self.driver = driver
self.engine = False
self.meters = 0
self.turnOnEngine()
def turnOnEngine(self):
if self.checkEngine():
self.engine = True
print("Engine Started!")
def checkEngine(self):
return True
def drive(self, distance):
if self.engine:
self.meters += distance
print("Drive %d kilometers."%distance)
else:
print('Engine is not turned on.')
def turnOffEngine(self):
self.engine = False
print("Engine has been turned off.")
def whoisDriving(self):
print('%s is driving the car.'%self.driver)
return self.driver
def getMeters(self):
return self.meters
myCar = Car("ABC-0311","Jack")
print('=====')
myCar.drive(100)
print('=====')
driverName = myCar.whoisDriving()
print('=====')
myCar.turnOffEngine()
print('=====')
myCar.drive(100)
print('=====')
myCar.turnOnEngine()
print('=====')
myCar.drive(100)
print('=====')
meter = myCar.getMeters()
print("Meters:",meter)
Output:
Engine Started!
=====
Drive 100 kilometers.
=====
Jack is driving the car.
=====
Engine has been turned off.
=====
Engine is not turned on.
=====
Engine Started!
=====
Drive 100 kilometers.
=====
Meters: 200
將物件的內部狀態和行為隱藏在物件內部,只公開必要的方法給外界使用。封裝可以保護物件免於外界的非法存取,並且讓物件更容易維護和修改。
class Animal:
name = ''
__private = '' # This cannot be accessed from the outside
def __init__(self, name):
self.name = name
self.__private = '' # This cannot be accessed from the outside
子類別可以繼承父類別的屬性和方法,並且可以擴展或覆寫父類別的行為。繼承可以提高程式碼重複使用性,並且可以讓類別之間建立階層關係,方便對類別進行分類和管理。
class Animal:
name = ''
def __init__(self, name):
self.name = name
def walk(self):
print('walking')
def eat(self):
print('eating')
class Dog(Animal):
def __init__(self, name):
super().__init__(name)
A = Dog('A')
A.walk()
A.eat()
print(A.name)
Output:
walking
eating
A
同樣的方法名稱可以在不同的類別中有不同的實現方式,這稱為多型。多型可以讓程式碼更加靈活,並且可以讓不同的物件對相同的方法有不同的行為。多型可以通過繼承和介面實現,是物件導向設計中非常重要的概念。
class Animal:
name = ''
def __init__(self, name):
self.name = name
def walk(self):
print('walking')
def eat(self):
print('eating')
class Dog(Animal):
def __init__(self, name):
super().__init__(name)
def walk(self):
print('{0} is using foot to walk'.format(self.name))
def eat(self):
print('{0} is eating bone'.format(self.name))
class Duck(Animal):
def __init__(self, name):
super().__init__(name)
def walk(self):
print('{0} is using two feet to walk'.format(self.name))
def eat(self):
print('{0} is eating worm'.format(self.name))
A = Dog('A')
B = Duck('B')
A.eat()
B.eat()
Output:
A is eating bone
B is eating worm
Code Source: 搞懂Python的OOP
到這裡為止你已經學完絕大部分常用的 Python 語法了,簡單開發所需的語法基本上不太會超過本篇教學的範圍。然而,資訊工程的領域極其廣大,目前碰到的還僅止於皮毛,若有興趣可以繼續鑽研資料結構、演算法等等課題,也可以透過題目或專案來練習自己的 Coding 能力。另外,網路上有很多相關資訊或教學,透過網路自我學習、不斷成長,也是件很重要的事情,加油!