在 Y 分鐘內學會 Python

在 Y 分鐘內學會 Python

這是翻譯, 原文地址: Learn Python in Y Minutes

在 90 年代初, Python 由 Guido van Rossum 創造, 現在, 它是最受歡迎的程式語言之一. 因其簡明的語法, 我愛上了它. 語法基本上是可以執行的偽程式碼.

提示: 這篇文章適用於 Python 3, 如果你想要學習舊版 Python 2.7, 單擊這裡

# 單行注釋以 '#' 作為開頭

"""多行注釋可以使用三個雙引號
   並且經常用與書寫文檔
"""

####################################################
## 1. 原始數據類型和操作符
####################################################

# 你可以使用數字
3  # 等同於 3

# 數學運算也是你希望使用的形式
1 + 1  # 結果是 2
8 - 1  # 結果是 7
10 * 2  # 結果是 20
35 / 5  # 結果是 7.0

# 正負數的整除都會向下取整
5 // 3  # 結果是 1
-5 // 3  # 結果是 -2
5.0 // 3.0  # 結果是 1.0 # 在浮點運算中也同樣生效
-5.0 // 3.0  # 結果是 -2.0

# 除法的運算結果始終是浮點數
10.0 / 3  # 結果是 3.3333333333333335

# 取模運算
7 % 3  # 結果是 1
# i % j 結果的符號與 j 相同, 這與 C 語言不同
-7 % 3  # 結果是 2

# 冪運算 (x**y, x的y次方)
2 ** 3  # 結果是 8

# 用括弧來強制優先運算
1 + 3 * 2  # 結果是 7
(1 + 3) * 2  # 結果是 8

# 布爾值是基本數據類型
True  # 值為 真(true)
False  # 值為 假(false)

# 使用 not 來進行非運算
not True  # 結果是 假
not False  # 結果是 真

# 布爾值操作符:
# 提示, 'and' 和 'or' 是區分大小寫的
True and False  # 結果是 假
False or True  # 結果是 真

# True 和 False 事實上也等同於 1 和 0, 只不過是使用了不同的關鍵字
True + True  # 結果是 2
True * 8  # 結果是 8
False - 5  # 結果是 -5

# 比較運算符會檢查 True 和 False 的數字值
0 == False  # 結果是 真
1 == True  # 結果是 真
2 == True  # 結果是 假
-5 != False  # 結果是 真

# 對整數使用布爾邏輯操作符, 則會將它們轉換為布爾值以求值,但返回未轉換的值
# 不要把 bool(ints) 和 位運算 and/or 搞混了
bool(0)  # 返回 假
bool(4)  # 返回 真
bool(-6)  # 返回 真
0 and 2  # 返回 0
-5 or 0  # 返回 -5

# 運算符 '等同於' 是 ==
1 == 1  # 返回 真
2 == 1  # 返回 假

# 運算符 '不等於' 是 !=
1 != 1  # 返回 假
2 != 1  # 返回 真

# 更多比較運算符
1 < 10  # 返回 真
1 > 10  # 返回 假
2 <= 2  # 返回真
2 >= 2  # 返回真

# 檢查一個值是否在指定範圍內
1 < 2 and 2 < 3  # 返回 真
1 < 3 and 3 < 2  # 返回 假
# 連接起來, 這樣看起來會更好看些
1 < 2 < 3  # 返回 真
2 < 3 < 2  # 返回 假

# (is 與 ==) is 將會檢查兩個變數是否引用了同一個對象, 但是 == 檢查
# 兩個對象是否指向了相同的值
a = [1, 2, 3, 4]  # 使 a 指向一個新的列表, [1, 2, 3, 4]
b = a  # 使 b 指向 a 所指向的對象
b is a  # 返回 真, a 和 b 引用的是同一個對象
b == a  # 返回 真, a 和 b 的對象是相等的
b = [1, 2, 3, 4]  # 使 b 指向一個新的列表, [1, 2, 3, 4]
b is a  # 返回 假, a 與 b 並不引用同一個對象
b == a  # 返回 真, a 和 b 的對象使相等的

# 字元串可以使用 雙引號 " 或 單引號 ' 來創建
"這是一個字元串"
'這也是一個字元串'

# 字元串可以相加
"Hello " + "world!"  # 返回 "Hello world!"
"Hello " "world!"  # 等同於 "Hello world!"

# 字元串可以用作一個字元列表
"Hello world!"[0]  # 返回 'H'

# 你可以獲取字元串的長度:
len("這是一個字元串")  # 返回 7

# 你可以使用 f-字元串 或 格式化字元串 來對字元串文本進行格式化 (在 Python 3.6 或更高版本)
name = "小紅"
f"她說她的名字是{name}."  # 返回 "她說她的名字是小紅."
# 你可以基本的將 Python 表達式放到括弧內, 然後它就會被輸出到字元串中.
f"{name}是{len(name)}字元長."  # 返回 "小紅是兩個字元長"

# None 是一個對象
None  # 返回 None

# 不要使用等同於運算符 '==' 來比較對象和 None
# 使用 'is' 來代替. 這個會檢查對象標識是否相同.
"etc" is None  # 返回 假
None is None  # 返回 真

# None, 0, 以及空的字元串/列表/字典/元組, 都計算為 假
bool(0)  # => 假
bool('')  # => 假
bool([])  # => 假
bool({})  # => 假
bool(())  # => 假

####################################################
## 2. 變數和集合
####################################################

# Python 有一個 print 函數, 用於標準輸出
print("我是Python, 很高興見到你!")  # => 我是Python, 很高興見到你!

# 默認的, print 函數還會在結尾輸出一個換行符
# 使用可選參數 'end' 來改變末尾的字元串.
print("Hello, world", end="!")  # => Hello, world!   # 輸出後沒有換行

# 從控制台獲取輸入數據的簡單方式:
input_string_var = input("Enter some data:")  # 以字元串的形式返回輸入數據

# Python 中沒有變數的聲明, 只有賦值.
# 命名規範是使用小寫以及下劃線, 就像這樣: lower_case_with_underscores
some_var = 5
some_var  # => 5

# 訪問一個沒有生命的變數是錯誤的
# 查看控制流來獲取更多異常捕捉資訊
some_unknown_var  # 這是一個一個未定義的變數, 運行時將拋出 NameError 異常

# if 也可用作一個表達式
# 等同於 C 語言中的 '?:' 三元運算符
"yay" if 0 > 1 else "nay!"  # => "nay"

# 列表可以存儲一個序列, 可以這樣定義:
li = []
# 你可以為其指定初始值
other_li = [4, 5, 6]

# 使用 append 方法在列表的末尾添加一些什麼東西
li.append(1)  # 現在 li 的值是 [1]
li.append(2)  # 現在 li 的值是 [1, 2]
li.append(4)  # 現在 li 的值是 [1, 2, 4]
li.append(3)  # 現在 li 的值是 [1, 2, 4, 3]
# 使用 pop 方法從列表的末尾移除元素
li.pop()  # 返回 3, 並且現在 li 的值是 [1, 2, 4]
# 重新將它放回去
li.append(3)  # 現在 li 又變成 [1, 2, 4, 3] 了

# 像訪問數組一樣訪問一個列表
li[0]  # => 1
# 訪問列表的最後一個元素
li[-1]  # => 3

# 如果索引超出界限, 那麼會拋出 IndexError 異常
li[4]  # 拋出 IndexError 異常

# 你可以使用切片語法來查看一個範圍內的元素
# 起始索引包含在內, 但結束索引不包含在內
# (對於數學類型來講, 它是一個 閉/開 區間)
li[1:3]  # 返回從索引1到3的一個列表 => [2, 4]
li[2:]  # 返回從索引2開始的列表 => [4, 3]
li[:3]  # 返回從開始到索引3的列表 => [1, 2, 4]
li[::2]  # 返回一個每兩個元素選擇一個的列表 => [1, 4]
li[::-1]  # 返回反向排列的列表 => [3, 4, 2, 1]
# 使用任意組合來實現高級切片
# li[起始:結束:步長]

# 使用切片來創建一個單層的深度拷貝
li2 = li[:]

# 使用 "del" 來刪除任意元素
del li[2]  # 現在 li 的值是 [1, 2, 3]

# 刪除第一個匹配值的元素
li.remove(2)  # 現在 li 的值是 [1, 3]
li.remove(2)  # 拋出 ValueError 異常, 2 並不在這個列表中

# 在指定索引處插入元素, 列表.insert(索引, 元素)
li.insert(1, 2)  # 現在 li 的值又是 [1, 2, 3] 了

# 獲取第一個匹配元素的索引
li.index(2)  # => 1
li.index(4)  # 拋出 ValueError 異常, 4 不在這個列表中

# 你可以將列表相加
# 提示: values 和 other_li 的值不會被修改
li + other_li  # => [1, 2, 3, 4, 5, 6]

# 使用 "extend()" 連接列表, extend 的意思是拓展
li.extend(other_li)  # 現在 li 的值是 [1, 2, 3, 4, 5, 6]

# 使用 "in" 檢查元素是否存在於列表中
1 in li  # => True

# 使用 "len()" 檢查列表的長度
len(li)  # => 6

# 元組與列表相像, 但是不可更改
tup = (1, 2, 3)
tup[0]  # => 1
tup[0] = 3  # 拋出一個 TypeError

# 提示, 長度為一的元組的最後一個元素必須有一個逗號跟隨, 但是
# 其他長度的元組, 儘管是0, 也不需要
type((1))  # => <class 'int'>
type((1,))  # => <class 'tuple'>
type(())  # => <class 'tuple'>

# 大多數的列表操作符都可以在元組上使用
len(tup)  # => 3
tup + (4, 5, 6)  # => (1, 2, 3, 4, 5, 6)
tup[:2]  # => (1, 2)
2 in tup  # => True

# 你可以將元組(或者列表)解包為變數
a, b, c = (1, 2, 3)  # 現在, a 是 1, b 是 2, c 是 3
# 你還可以使用拓展解包
a, *b, c = (1, 2, 3, 4)  # 現在, a 是 1, b 是 [2, 3], c 是 4
# 默認情況下, 即便你忽略括弧, 也會創建一個元組
d, e, f = 4, 5, 6  # 元組 4, 5, 6 被解包為變數 d, e, f
# 變數值分別如下: d = 4, e = 5 以及 f = 6
# 那麼看看交換兩個值是多麼的簡單
e, d = d, e

# 字典用於存儲從鍵到值的映射
empty_dict = {}  # 創建了一個空字典
# 下面是一個帶有初始值的字典
filled_dict = {"one": 1, "two": 2, "three": 3}

# 提示: 字典的鍵必須是不可變的類型.  這是為了確保
# 鍵能夠轉換為用於快速查詢的常量哈希值.
# 不可變類型包含整數, 浮點數, 字元串, 元組
invalid_dict = {[1, 2, 3]: "123"}  # 拋出 TypeError: unhashable type: 'list' 異常. (類型錯誤: 無法進行哈希化的類型: '列表')
valid_dict = {(1, 2, 3): [1, 2, 3]}  # 然而, 值可以是任何類型

# 使用 [] 來查詢值
filled_dict["one"]  # => 1

# 使用 "keys()" 來獲取所有的鍵並作為一個可迭代對象返回. 我們需要在 list() 中將調用結果轉換
# 為一個列表. 這個稍後再講. 提示 - 在 Python 低於 3.7 的版本中
# 字典鍵索引的順序是無法保證的. 你的結果可能不與下面的例子完全相等. 然而, 在 Python 3.7 中, 字典
# 元素會保持它們被插入到字典的順序
list(filled_dict.keys())  # => ["three", "two", "one"] 在低於 3.7 的 Python 版本中
list(filled_dict.keys())  # => ["one", "two", "three"] 在 3.7 或更高的 Python 版本中

# 使用 "values()" 來獲取所有的值並作為可迭代對象返回. 同樣, 我們需要將其在
# list() 中轉換, 以取出這個可迭代對象的值. 提示 - 和上面鍵的順序是一樣的
list(filled_dict.values())  # => [3, 2, 1] 在低於 3.7 的 Python 版本中
list(filled_dict.values())  # => [1, 2, 3] 在 3.7 或更高的 Python 版本中

# 使用 "in" 來檢查鍵是否在字典中
"one" in filled_dict  # => True
1 in filled_dict  # => False

# 通過不存在的鍵來查找字典, 會拋出 KeyError 異常
filled_dict["four"]  # KeyError

# 使用 "get()" 方法來避免 KeyError 異常
filled_dict.get("one")  # => 1
filled_dict.get("four")  # => None
# 這個方法支援當找不到值時返回指定的默認值
filled_dict.get("one", 4)  # => 1
filled_dict.get("four", 4)  # => 4

# "setdefault()" 只有在給定鍵不存在的時候將值插入到字典
filled_dict.setdefault("five", 5)  # filled_dict["five"] 被設置為了 5
filled_dict.setdefault("five", 6)  # filled_dict["five"] 仍然是 5

# 向字典中添加內容
filled_dict.update({"four": 4})  # => {"one": 1, "two": 2, "three": 3, "four": 4}
filled_dict["four"] = 4  # 向字典中添加的另一種方式

# 自 Python 3.5 以來, 你還可以使用拓展解包選項
{'a': 1, **{'b': 2}}  # => {'a': 1, 'b': 2}
{'a': 1, **{'a': 2}}  # => {'a': 2}

# 集合用來存儲 ... 額, 集合
empty_set = set()  # 空集合
# 用一組值來初始化一個集合, 嗯, 看起來有點像字典, 抱歉
some_set = {1, 1, 2, 2, 3, 4}  # some_set 現在的值是 {1, 2, 3, 4}

# 與字典中的鍵相似, 集合的元素必須是不可變的
invalid_set = {[1], 1}  # => 拋出一個 TypeError: unhashable type: 'list' (類型錯誤: 無法進行哈希化的類型: '列表')
valid_set = {(1,), 1}

# 向集合中添加一個或多個條目
filled_set = some_set
filled_set.add(5)  # filled_set 現在是 {1, 2, 3, 4, 5}
# 集合是不包含重複元素的
filled_set.add(5)  # 還是與之前一樣 {1, 2, 3, 4, 5}

# 使用 & 取交集
other_set = {3, 4, 5, 6}
filled_set & other_set  # => {3, 4, 5}

# 使用 | 取並集
filled_set | other_set  # => {1, 2, 3, 4, 5, 6}

# 使用 - 取差集
{1, 2, 3, 4} - {2, 3, 5}  # => {1, 4}

# 使用 ^ 取對稱差集
{1, 2, 3, 4} - {2, 3, 5}  # => {1, 4, 5}

# 檢查左側的集合是否是右側集合的超集
{1, 2} >= {1, 2, 3}  # => False

# 檢查左側的集合是否是右側集合的子集
{1, 2} <= {1, 2, 3}  # => True

# 使用 in 來檢查是否存在於集合中
2 in filled_set  # => True
10 in filled_set  # => False

# 生成一個單層的深層副本
filled_set = some_set.copy()  # filled_set 是 {1, 2, 3, 4, 5}
filled_set is some_set  # => False

####################################################
## 3. 控制流和可迭代對象
####################################################

# 首先我們聲明一個變數
some_var = 5

# 這是一個 if 語句, 縮進在 Python 中非常重要
# 約定語法是使用四個空格, 而不是水平製表符(tab)
# 這個將會列印 "some_var 比 10 小"
if some_var > 10:
    print("some_var 比 10 大")
elif some_var < 10:  # 這個 elif 語句是可選的
    print("some_var 比 10 小")
else:  # 這個也是可選的
    print("some_var 與 10 相等")

"""
for 語句用來循環遍歷列表
將會列印:
    狗是哺乳動物
    貓是哺乳動物
    老鼠是哺乳動物
"""
for animal in ["狗", "貓", "老鼠"]:
    # 你可以使用 format() 來插入格式化字元串
    print("{}是哺乳動物".format(animal))

"""
"range(數字)" 返回一個數字的可迭代對象
從0到給定數字
將會列印:
    0
    1
    2
    3
"""
for i in range(4):
    print(i)

"""
"range(較小的數, 較大的數)" 返回一個數字的可迭代對象
從較小的數字到較大的數字
將會列印:
    4
    5
    6
    7
"""
for i in range(4, 8):
    print(i)

"""
"range(較小的數, 較大的數, 步長)" 返回一個數字的可迭代對象
從較小的數到較大的數, 以步長未每次增長的值
如果步長沒有指定, 默認值則是 1
將會列印:
    4
    6
"""
for i in range(4, 8, 2):
    print(i)

"""
循環一個列表, 並且同時檢索列表中每一個條目的索引和值
將會列印:
    0 狗
    1 貓
    2 老鼠
"""
animals = ["狗", "貓", "老鼠"]
for i, value in enumerate(animals):
    print(i, value)

"""
while 循環將一直進行到條件不再滿足為止
將會列印:
    0
    1
    2
    3
"""
x = 0
while x < 4:
    print(x)
    x += 1  # x = x + 1 的簡寫

# 使用 try/except 語句塊來處理異常
try:
    # 使用 "raise" 來拋出異常
    raise IndexError("這是一個索引錯誤")
except IndexError as e:
    pass  # pass 只是一個佔位符(不進行任何操作). 通常你需要在這裡對異常進行處理
except (TypeError, NameError):
    pass  # 如果需要, 你可以同時處理多個異常.
else:  # try/except 語句塊的可選語句. 必須在所有 except 語句塊的後面
    print("一切正常!")  # 僅在 try 語句內沒有任何異常時運行
finally:  # 在任何情況下都會執行
    print("我們可以在這裡進行資源清理")

# 你可以使用 with 語句代替 try/finally 來清理資源
with open("我的文件.txt") as f:
    for line in f:
        print(line)

# 向文件中寫入內容
contents = {"aa": 12, "bb": 21}
with open("我的文件1.txt", "w+") as file:
    file.write(str(content))  # 向文件中寫入字元串

with open("我的文件2.txt", "w+") as file:
    file.write(json.dumps(content))  # 向文件中寫入一個對象

# 從文件中讀取
with open("我的文件1.txt", "r+") as file:
    contents = file.read()  # 從文件中讀取一個字元串
print(contents)
# 列印: {"aa", 12, "bb": 21}

with open("我的文件2.txt", "r+") as file:
    contents = json.load(file)  # 從文件中讀取一個json對象
print(contents)
# print: {"aa": 12, "bb": 21}


# Python 提供了一個叫做 Iterable(可迭代的) 的基本抽象
# 一個可迭代對象是一個可視為序列的對象
# range 函數返回的對象就是一個可迭代對象

filled_dict = {"one": 1, "two": 2, "three": 3}
our_iterable = filled_dict.keys()
print(our_iterable)  # => dict_keys(['one', 'two', 'three']). 這是一個實現了 Iterable 介面的對象

# 我們可以檢索它
for i in our_iterable:
    print(i)  # 列印 one, two, three

# 然而, 我們不可以通過索引來找到元素
our_iterable[1]  # 拋出 TypeError 異常

# 一個可迭代對象即為能夠創建迭代器的對象
our_iterator = iter(our_iterable)

# 迭代器是一個能夠記住當前通過它迭代狀態的對象
# 我們可以通過 "next()" 來獲取下一個對象
next(our_iterator)  # => "one"

# 它會保持我們遍歷的狀態
next(our_iterator)  # => "two"
next(our_iterator)  # => "three"

# 在迭代器已經返回完所有的數據後, 將會拋出 StopIteration 異常
next(our_iterator)  # 拋出 StopIteration 異常

# 我們也可以檢索它, 事實上, "for" 語句就是隱式的執行了這個操作
our_iterator = iter(our_iterable)
for i in iterator:
    print(i)

# 你可以通過調用 list() 來獲取可迭代對象或迭代器的所有元素.
list(our_iterable)  # => 返回 ["one", "two", "three"]
list(our_iterator)  # => 返回 [] 因為迭代狀態已經被保存下來


####################################################
## 4. 函數
####################################################

# 使用 "def" 來創建一個新的函數
def add(x, y):
    print("x 是 {} 以及 y is {}".format(x, y))
    return x + y


# 調用帶參數的函數
add(5, 6)  # => 輸出 "x 是 5 以及 y 是 6", 並返回 11

# 調用函數的另一種方式是帶有關鍵字參數
add(y=6, x=5)  # 關鍵字參數可以在任何順序下正常運行


# 你可以定義接受數量可變的位置參數的函數
def varargs(*args):
    return args


varargs(1, 2, 3)  # => (1, 2, 3)


# 同樣, 你可以定義接受數量可變的關鍵字參數的函數
def keyword_args(**kwargs):
    return kwargs


# 來調用一下, 然後看看會發生什麼
keyword_args(big="foot", loch="ness")  # => {"big": "foot", "loch": "ness"}


# 只要你想, 你也可以同時使用它們兩個
def all_the_args(*args, **kwargs):
    print(args)
    print(kwargs)


"""
all_the_args(1, 2, a=3, b=4) 將會列印:
    (1, 2)
    {"a": 3, "b": 4}
"""

# 在調用函數時, 你可以做相反的 args/kwargs!
# 使用 * 來拓展元組, 以及使用 ** 來拓展 kwargs.
args = (1, 2, 3, 4)
kwargs = {"a": 3, "b": 4}
all_the_args(*args)  # 等同於 all_the_args(1, 2, 3, 4)
all_the_args(**kwargs)  # 等同於 all_the_args(a=3, b=4)
all_the_args(*args, **kwargs)  # 等同於 all_the_args(1, 2, 3, 4, a=3, b=4)


# 返回多個值(通過賦值元組)
def swap(x, y):
    return y, x  # 通過沒有括弧的元組來返回多個值
    # (提示: 括弧雖然沒有寫, 但是也可以添加上)


x = 1
y = 2
x, y = swap(x, y)  # => x = 2, y = 1
# (x, y) = swap(x,y)  # 同樣, 括弧雖沒有寫, 但是也可以添加上

# 函數作用域
x = 5


def set_x(num):
    # 局部變數 x 與全局變數 x 是不同的
    x = num  # => 43
    print(x)  # => 43


def set_global_x(num):
    global x
    print(x)  # => 5
    x = num  # => 全局變數 x 現在被設置為 6 了
    print(x)  # => 6


set_x(43)
set_global_x(6)


# Python 支援本地函數
def create_adder(x):
    def adder(y):
        return x + y

    return adder


add_10 = create_adder(10)
add_10(3)  # => 13

# 也支援匿名函數
(lambda x: x > 2)(3)  # => True
(lambda x, y: x ** 2 + y ** 2)(2, 1)  # => 5

# 下面是一些內置的高階函數
list(map(add_10, [1, 2, 3]))  # => [11, 12, 13]
list(map(max, [1, 2, 3], [4, 2, 1]))  # => [4, 2, 3]

list(filter(lambda x: x > 5, [3, 4, 5, 6, 7]))  # => [6, 7]

# 你可以使用列表推導式來實現優秀的映射與過濾
# 列表推導式存儲可嵌套的列表以輸出
[add_10(i) for i in [1, 2, 3]]  # => [11, 12, 13]
[x for x in [3, 4, 5, 6, 7] if x > 5]  # => [6, 7]

# 同樣, 你可以構建集合和字典推導式
{x for x in 'abcddeef' if x not in 'abc'}  # => {'d', 'e', 'f'}
{x: x ** 2 for x in range(5)}  # => {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

####################################################
## 5. 模組
####################################################

# 導入模組, 調用模組中的函數
import math

print(math.sqrt(16))  # => 返回4.0

# 你可以從模組中獲取指定的函數
from math import ceil, floor

print(ceil(3.7))  # => 4.0
print(floor(4.7))  # => 3.0

# 你可以從模組中導入所有函數
# 警告: 不推薦這麼做
from math import *

# 你可以在導入模組時, 為其起一個別名
import math as m

math.sqrt(16) == m.sqrt(16)  # => True

# Python 模組只是普通的 Python 文件. 你
# 可以編寫你自己的模組, 然後導入它們. 模組
# 的名字與文件名是相同的

# 你可以查找模組內定義的函數和屬性
import math

dir(math)


# 如果有一個名為 math.py 的 Python 腳本與
# 當前腳本在同一個目錄下, 那麼這個文件將會
# 代替 Python 內建模組以載入.
# 這是因為本地目錄有比 Python 的內建庫更高
# 的優先順序.


####################################################
## 6. 類
####################################################

# 使用 "class" 語句來創建一個類
class Human:
    # 類的屬性. 將在類的所有實例中共享
    species = "SlimeNull"

    # 基本的初始化器, 這個將會在類實例化時調用
    # 提示, 雙前置和後置下劃線表示 Python 使用
    # 的對象或屬性, 但是它們存在於用戶控制的命名
    # 空間. 方法(或者是對象, 屬性) 像 __init__, __str__,
    # __repr__ 等等, 被稱為特殊方法(有時也成為魔法方法)
    # 你不應該創建你自己的, 與它們重名的成員
    def __init__(self, name):
        # 將參數賦值給實例的 name 屬性
        self.name = name

        # 初始化屬性
        self._age = 0

    # 實例的方法. 所有的方法都需要使用 "self" 作為第一個參數
    def say(self, msg):
        print("{name}: {message}".format(name=self.name, message=msg))

        # 實例的另一個方法
        def sing(self):
            return '喲... 喲... 麥克風檢查... one... one two...'

    # 類的方法, 所有的實例都能夠訪問
    # 它們被調用時, 調用它的類將作為調用的第一個參數
    @classmethod
    def get_species(cls):
        return cls.species;

    # 靜態方法在調用時, 不會有類或實例的引用傳入
    @staticmethod
    def grunt():
        return "*咕嚕咕嚕*"

    # 屬性就像一個獲取器(getter)
    # 它將這個方法 age() 作為一個同名的, 只讀的屬性返回
    # 但是, 在 Python 中不需要寫繁瑣的獲取器和設置器(setter)
    @property
    def age(self):
        return self._age

    # 這個將允許屬性被設置
    @age.setter
    def age(self, age):
        self._age = age

    # 這個將允許屬性被刪除
    @age.deleter
    def age(self):
        del self._age


# 當 Python 解釋器讀取源文件並執行時
# __name__ 會確保這個程式碼塊在模組中
# 是作為主程式執行的
if __name__ == '__main__':
    # 實例化類
    i = Human(name="小明")
    i.say("嗨~")
    j = Human("小紅")
    j.say("你好哇~")
    # i 和 j 是類型 Human 的實例, 換句話說, 他們都是 Human 對象

    # 調用類的方法
    i.say(i.get_species())          # => "小明: SlimeNull"
    # 更改共享屬性
    Human.species = "Little Wu♂ Fairy"
    i.say(i.get_species())          # => "小明: Little Wu♂ Fairy"
    j.say(j.get_species())          # => "小紅: Little Wu♂ Fairy"

    # 調用靜態方法
    print(Human.grunt())            # => "*咕嚕咕嚕*"

    # 靜態方法也可以通過實例來調用
    print(i.grunt())                # => "*咕嚕咕嚕*"

    # 為這個實例更新屬性
    i.age = 42
    # 獲取屬性
    i.say(i.age)                    # => "小明: 42"
    j.say(j.age)                    # => "小紅: 0"
    # 刪除屬性
    del i.age
    # i.age                         # => 將會拋出 AttributeError 異常


####################################################
## 6.1 繼承
####################################################

# 繼承允許定義的新的子類從父類繼承
# 方法與變數

# 使用上面定義的 Human 類作為基類(父類), 我們可以定義
# 一個子類, Superhero, 它將繼承 Human 類的變數, 例如
# "species", "name", 以及 "age", 方法也是如此, 例如
# "sing" 與 "grunt". 但同時它也可以有自己的特殊屬性.

# 你可以將上面的類存儲到它們自己的文件中來採用模組化文件的優點
# 命名為, human.py

# 從別的文件中導入函數, 需要使用下面的格式
# from "沒有後綴的文件名" import "函數或者類"

from human import Human


# 在類型定義處以參數的形式指定父類
class Superhero(Human):

    # 如果字類僅僅是從父類繼承所有定義, 且沒有
    # 任何更改, 你可以只在這裡寫一個 "pass" 關鍵字 (別的不需要寫)
    # 但在我們這種情況下, pass 就要被注釋掉以為 Superhero 類創建
    # 它特有的內容
    # pass

    # 字類可以覆蓋父類的屬性
    species = "Superhuman"

    # 字類會自動的從父類繼承構造函數, 包括
    # 它的參數, 但是也可以定義另外的參數, 或者定義
    # 然後覆蓋它的方法, 例如這個類的構造函數
    # 這個構造函數從 Human 類繼承了 "name" 參數並且
    # 添加了 "superpower" 和 "movie" 參數
    def __init__(self, name, movie=False,
                 superpowers = ["力大無窮", "金剛不壞之身"]):

        # 添加額外的類型屬性
        self.fictional = True
        self.movie = movie
        # 注意可變的默認值, 因為它們默認是共享的
        self.superpowers = superpowers

        # "super" 函數使你可以訪問被字類重寫了的
        # 父類的方法, 在這裡, 我們要使用 "__init__"
        # 這將會調用父類的構造函數
        super().__init__(name)

    # 覆蓋 sing 方法
    def sing(self):
        return "Dun, dun, DUN!"

    # 添加附加的實例方法
    def boast(self):
        for power in self.superpowers:
            print("我有{pow}的能力!".format(pow=power))


if __name__ == '__main__':
    sup = Superhero(name="蒂克")

    # 實例類型檢查
    if isinstance(sup, Human):
        print("我是人類")
    if type(sup) is Superhero:
        print("我是一個超級英雄")

    # 通過使用 getattr() 和 super() 來獲取方法解析搜索順序
    # 這個屬性是動態的, 並且可以被更新
    print(Superhero.__mro__)    # => (<class '__main__.Superhero'>,
                                # => <class 'human.Human'>, <class 'object'>)

    # 通過它自己的類型屬性來調用父類的方法
    print(sup.get_species())    # => Superhuman

    # 調用被重寫了的方法
    print(sup.sing())           # => Dun, dun, DUN!

    # 調用 Human 的方法
    sup.say("勺子?")             # => 蒂克: 勺子?

    # 調用僅存在於 Superhero 中的方法
    sup.boast()                 # => 我有力大無窮的能力!
                                # => 我有金剛不壞之身的能力!

    # 繼承的類型屬性
    sup.age = 32
    print(sup.age)              # => 32

    # 僅在 Superhero 中存在的屬性
    print('我能獲得奧斯卡獎嗎?' + str(sup.movie))

####################################################
## 6.2 多重繼承
####################################################

# 另一個類的定義
# bat.py
class Bat:

    species = '貝蒂'

    def __init__(self, can_fly=True):
        self.fly = can_fly

    # 這個類也有一個說的方法
    def say(self, msg):
        msg = '... ... ...'
        return msg

    # 並且它也有自己的方法
    def sonar(self):
        return '))) ... ((('

if __name__ == '__main__':
    b = Bat()
    print(b.say('你好'))
    print(b.fly)


# 然後現在另一個類型定義繼承自 Superhero 和 Bat
# superhero.py
from superhero import Superhero
from bat import Bat

# 定義 Batman 並同時繼承 Superhero 和 Bat
class Batman(Superhero, Bat):

    def __init__(self, *args, **kwargs):
        # 繼承屬性通常需要 super
        # super(Batman, self).__init__(*args, **kwargs)
        # 然而在這裡處理多繼承, 所以 super()
        # 僅僅適用於 MRO 列表的下一個基類
        # 所以, 我們需要為所有父類顯式的調用 __init__
        # *args 和 **kwargs 允許使用非常簡潔的方式傳遞所有參數,
        # 每一個父類都 "給洋蔥剝一層皮"
        Superhero.__init__(self, 'anonymous', movie=True,
                           superpowers=['Wealthy'], *args, **kwargs)
        Bat.__init__(self, *args, can_fly=False, **kwargs)
        # 覆蓋相同名稱的屬性的值
        self.name = "悲傷的阿弗萊克"

    def sing(self):
        return "吶 吶 吶 吶 吶 蝙蝠俠!"


if __name__ == '__main__':
    sup = Batman()

    # 通過使用 getattr() 和 super() 來獲取方法解析搜索順序
    # 這個屬性是動態的, 並且可以被更新
    print(Batman.__mro__)       # => (<class '__main__.Batman'>,
                                # => <class 'superhero.Superhero'>,
                                # => <class 'human.Human'>,
                                # => <class 'bat.Bat'>, <class 'object'>)

    # 通過它自己的類型屬性來調用父類的方法
    print(sup.get_species())    # => Superhuman

    # 調用被重寫了的方法
    print(sup.sing())           # => 吶 吶 吶 吶 吶 蝙蝠俠!

    # 調用 Human 的方法, 原因是繼承問題
    sup.say('我同意')            # => 悲傷的阿弗萊克: 我同意

    # 調用存在於第二個祖先的方法
    print(sup.sonar())          # => ))) ... (((

    # 繼承了的類型屬性
    sup.age = 100
    print(sup.age)              # => 100

    # 從第二個祖先繼承的默認值被重寫了的屬性
    print('我能飛嗎?' + str(sup.fly))  # => 我能飛嗎? 不能



####################################################
## 7. 高級
####################################################

# 生成器可以幫助你寫一些更簡便的程式碼 (偷懶)
def double_numbers(iterable):
    for i in iterable:
        yield i + i

# 生成器是 高效記憶體 的, 因為它們只載入需要的數據
# 處理可迭代對象的下一個值, 這使他們可以在非常大
# 的值域上執行操作.
# 提示: `range` 在 Python3 中取代了 `xrange`
for i in double(range(1, 900000000)):  # `range` 是一個生成器
    print(i)
    if (i > 30):
        break

# 就像你能創建列表推導式一樣, 你也可以創建
# 生成器推導式
values = (-x for x in [1,2,3,4,5])
for x in values:
    print(x)  # 列印 -1 -2 -3 -4 -5 到 控制台/終端

# 你也可以將生成器推導式轉換為一個列表
values = (-x for x in [1,2,3,4,5])
gen_to_list = list(values)
print(gen_to_list)  # => [-1, -2, -3, -4, -5]

# 裝飾器
# 在這個例子中, `beg` 與 `say` 交換. 如果 say_please 是 True, 那麼它
# 將會改變返回的消息
from functools import wraps


def beg(target_function):
    @wraps(target_function)
    def wrapper(*args, **kwargs):
        msg, say_please = target_function(*args, **kwargs)
        if say_please:
            return "{} {}".format(msg, "求你了, 我很窮 :(")
        return msg

    return wrapper


@beg
def say(say_please=False):
    msg = "能為我買瓶啤酒嗎?"
    return msg, say_please


print(say())                 # 能為我買瓶啤酒嗎?
print(say(say_please=True))  # 能為我買瓶啤酒嗎? 求你了, 我很窮 :(
Tags: