很粗略的說, 我們可以把 Class
想成一個「自訂資料型態」的方法。Python 什麼東西都是「物件」, 而在我們定義一個字串時, 其實是定義了一個「字串這個類別 (class)」的「實例 (instance)」。
st = "hello world"
這裡 st
是「字串」這個類別的一個實例。
所謂的方法 (method), 就是一個類別 (如字串) 中定義, 可以作用在這個實例 (變數) 身上的函式。
再一次, 我們可以把定一個 Class
看成自己定義新的資料型態的方法。比如說, 我們想定義一個「樸克牌 Class」, 也許可以這樣做...
class Card:
suit = 3
rank = 5
我們來定義一張牌 card01
。
card01 = Card()
card01.suit = 2
card01.rank = 5
card01.suit
2
card01.rank
5
我們甚至可以檢查 card01
的資料型態。
type(card01)
__main__.Card
我們可以再定義新的牌。
card02 = Card()
card02.suit = 3
card02.rank = 10
但這樣子也太麻煩了, 難道不能這樣:
card02 = Card(3, 10)
就定義一張牌? 答案當然是肯定的! 這時要動用 Python 的「特殊方法」, 這些在 Class 中使用的特殊方法, 都有一個特殊的命名方式, 那就是...
每個特殊方法都有特定使用場合, 比如說 __init__
就是在定義一個 class 的實例時會呼叫的函式。
class Card:
def __init__(self, s, r):
self.suit = s
self.rank = r
這裡要解釋的是, 所有在類別定義的方法 (函式), 一定要引用實例自己本身。這也是初學者最弄不清楚的地方。
而實例在定義 class 時根本也還沒有定義, 我們怎知該叫什麼呢? 於是一般都很聰明的稱為 self
。這其實不一定非叫 self
, 只是高手們大家都這樣叫, 我們也這樣叫看起來就好像很有涵養的樣子。
這樣子定義好, 我們就可以快速的定義一張樸克牌!
card01 = Card(2, 3)
card02 = Card(3, 10)
card01.suit
2
card01.rank
3
我們自己可能知道, suit 代表花色, 有四種所以是 0, 1, 2, 3。可是一般人怎麼知道這是什麼呢? 我們就來第一個自己寫的 class 方法, 用途就是印出花色、牌面點數。
class Card:
SUITS = ["♣", "♦", "♥", "♠"]
RANKS = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
def __init__(self, s, r):
self.suit = s
self.rank = r
def show(self):
print(self.SUITS[self.suit] + self.RANKS[self.rank])
card01 = Card(2, 3)
card01.show()
♥4
這是不是看來順眼多了? 不過當我們想看 card01
內容...
card01
<__main__.Card at 0x7fbbc80fdf28>
甚至印出 card01
內容...
print(card01)
<__main__.Card object at 0x7fbbc80fdf28>
為什麼會這樣呢? 因為我們根本沒有定義該怎麼表示 Card 這個類別裡的東西。要做到這件事, 要用另一個特殊方法:
__repr__
class Card:
SUITS = ["♣", "♦", "♥", "♠"]
RANKS = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
def __init__(self, s, r):
self.suit = s
self.rank = r
def __repr__(self):
return self.SUITS[self.suit] + self.RANKS[self.rank]
card01 = Card(2, 3)
card01
♥4
print(card01)
♥4
傑克, 這真的太神奇了!
不過這裡要說明一點, 其實很多時候你不一定非要把程式寫成 Class, 可以用一個或多個函式來做到你想做的事。
我們這裡介紹 Class, 最重要的其實是讓大家看懂別人用 Class 寫的程式。
print
不換行可以嗎?¶我們想印出一堆數字 (或文字)...
for i in range(10):
print(i)
0 1 2 3 4 5 6 7 8 9
這拖太長了, 可以不要換行嗎? 我們想到可愛的逗號...
for i in range(10):
print(i,)
0 1 2 3 4 5 6 7 8 9
這是在搞笑嗎?
原來, Python 印出來的字串要「某個方式接續」是這樣做...
for i in range(10):
print(i, end=', ')
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
這時果然被抓包, 說 9 的後面好像不該有逗點。對, 這都要例外處理。
雖然不是我們的重點, 不過為了老師的威信 (?), 所以還是介紹其中一種方式。
print(', '.join(map(str, range(10))))
0, 1, 2, 3, 4, 5, 6, 7, 8, 9
附註: 這要「背」還有點難。不過理解在做什麼, 就自己也會寫出看來很可怕的東西。
之前我們要控制一堆字串印出來, 就是要用 "+" 合在一起...
name = "炎龍"
age = 25
print("你好, 我是" + name + ", 今年 " + str(25) + " 歲。")
你好, 我是炎龍, 今年 25 歲。
這實在有夠難打的, 所以 Python 提供了格式化字串的方法, 而且有三種!
在要印出字串的地方打上 %s
, 要印出整數的地方打上 %d
, 要印出點數打上 %f
...
message = "你好, 我是%s, 今年 %d 歲。" % (name, age)
print(message)
你好, 我是炎龍, 今年 25 歲。
哦, 好像不錯耶! 不過要記得 %s
啦, %d
啦, %f
等等是什麼意思好像有點討厭。
format
¶直接在要填入東西的地方, 放上 {}
, 不用說這是字串啦、數字啦還是什麼的。
message = "你好, 我是{}, 今年 {} 歲。".format(name, age)
print(message)
你好, 我是炎龍, 今年 25 歲。
這好像更好一點, 不過有更方便的嗎?
f-string
¶f-string
是 Python 3.6 才有的方法。直接填入要什麼就有什麼...
message = f"你好, 我是{name}, 今年 {age} 歲。"
print(message)
你好, 我是炎龍, 今年 25 歲。
我們 Google 一下, 發現 1 美元合台幣 31.0665134 元。
c = 31.0665134
print(f"1 美元合台灣 {c} 元。")
1 美元合台灣 31.0665134 元。
更炫的事是, 我們可以控制只印出小數點後 2 位。這時的 f
(浮點數) 又出現了。
print(f"1 美元合台灣 {c:.2f} 元。")
1 美元合台灣 31.07 元。
注意還自動四捨五入! 再來我們看看以下這個例子。
print(f"1 美元合台灣 {c:10.2f} 元。")
1 美元合台灣 31.07 元。
看出來發生什麼事了嗎?
我們其實還可以控制靠左靠右的對齊...
star = "*"
print(f"{star:>9s}■")
*■
靠左。
star = "*"
print(f"{star:<9s}■")
* ■
置中。
star = "*"
print(f"{star:^9s}■")
* ■
還可以用指定符號填滿空格。
star = "*"
print(f"{star:.>9s}■")
........*■
常見的應用是數字前要補 0。
m = str(3)
d = str(8)
m_str = f"{m:0>2s}"
d_str = f"{d:0>2s}"
m_str
'03'
d_str
'08'
當然, 還可以更快的...
m.zfill(2)
'03'
利用置中的耍寶小應用。
for i in range(1, 14, 2):
stars = "*" * i
print(f'{stars:^13s}')
* *** ***** ******* ********* *********** *************
我們讓 Python 寫首詩吧。
去收集一些你覺得容易在詩裡出現的字眼, 做成一個串列。接著, 你可以亂數決定幾句的詩, 比如說 2-7 句。同時也可以亂數決定每句各用多少詞, 再隨機選取你選的字...
st = '''
我
我的
眼睛
妳
妳的
心
溫柔
日子
雨
風
天空
雲
等待
哭泣
戀愛
相遇
分離
忘記
心醉
驀然
吹過
思念
靈魂
停止
'''
words = st.split('\n')
words
['', '我', '我的', '眼睛', '妳', '妳的', '心', '溫柔', '日子', '雨', '風', '天空', '雲', '等待', '哭泣', '戀愛', '相遇', '分離', '忘記', '心醉', '驀然', '吹過', '思念', '靈魂', '停止', '']
from random import randint, choices, sample
n = randint(2, 7) # 決定有幾句
for i in range(n):
m = randint(2, 5) # 決定每句的長度
sentence = sample(words, m)
print(" ".join(sentence))
驀然 心 戀愛 吹過 靈魂 日子 心醉
然後我們改成函數。
def poem():
n = randint(2, 7) # 決定有幾句
for i in range(n):
m = randint(2, 5) # 決定每句的長度
sentence = sample(words, m)
print(" ".join(sentence))
poem()
靈魂 風 心 停止 心醉 戀愛 停止 吹過 停止 靈魂 我的 妳的
於是我們就可以一直寫詩...
poem()
哭泣 戀愛 驀然 我的 日子 眼睛 妳的 等待 我 思念 哭泣 心 風 戀愛 忘記 眼睛 天空 心 風 吹過
poem()
我的 分離 我 我的 天空 等待 我 思念 日子 心醉 心醉 溫柔 忘記 戀愛 風 相遇 溫柔 妳 溫柔 停止 戀愛
用之前學的存檔起來!
%save "poem.py" 62 59-60 66-67
The following commands were written to file `poem.py`: from random import randint, choices, sample st = ''' 我 我的 眼睛 妳 妳的 心 溫柔 日子 雨 風 天空 雲 等待 哭泣 戀愛 相遇 分離 忘記 心醉 驀然 吹過 思念 靈魂 停止 ''' words = st.split('\n') def poem(): n = randint(2, 7) # 決定有幾句 for i in range(n): m = randint(2, 5) # 決定每句的長度 sentence = sample(words, m) print(" ".join(sentence)) poem()
於是, 在終端機中, 我們就可以與詩相遇...