Python 廖雪峰: https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000
Python 的安裝及.........配置(Mac 系統(tǒng)下)
Mac 系統(tǒng)下的安裝
-
安裝 Homebrew
~ 理解 homebrew 和 npm 區(qū)別
homebrew: osx的軟件管理工具, 軟件管理助手, 可以安裝Chrome瀏覽器等可視化工具
npm: node.js的程序/模塊管理工具, 服務(wù)于JavaScript社區(qū)
~ 安裝 homebrew:
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
-
安裝 Python
~ python2.x 和 python3.x
python2.x: Mac 系統(tǒng)自帶 Python2.7(老, 被取代)
python3.x: ① 官網(wǎng)下載安裝 ② 通過 brew
brew install python3
安裝 -
Python 解釋器(交互式命令行)
CPython(下載安裝 python 即完成): 解釋器是用 C 語言開發(fā)的向胡,所以叫 CPython留攒。在命令行下運(yùn)行 python 就是啟動 CPython 解釋器
使用
終端中運(yùn)行
python
, 啟動 python2.x終端中運(yùn)行
python3
, 啟動 Python3.xCPython 使用
>>>
, 作為提示符, 使用exit()
作為退出 -
文本編輯器 (VScode)
安裝拓展
Python
vscode 報(bào)錯:
Linter pylint is not installed
(代碼規(guī)范的插件)
~ 參考:
https://blog.csdn.net/yh1061632045/article/details/81665446
~ 手動切換 python2.7 --> python3.7(地址是:
/usr/local/bin/python3
)~ 配置 vscode, 設(shè)置
python.pythonPath: /usr/local/bin/python3
~ 然后點(diǎn)擊
install pylint
, 安裝完成 vscode 就不彈出提示, 同時(shí)能代碼提示
win 系統(tǒng)下的安裝
官網(wǎng)下安裝
勾選
Add Python 3.7 to PATH
, 然后安裝即可在命令提示符中,
python
會出來>>>
即代表安裝成功可能報(bào)錯
code2305
第一個 Python 程序
-
步驟
添加
.py
文件, 編寫print('hello world!')
-
執(zhí)行.py 文件
利用 python 命令: cd 到文件所在目錄 -> 終端執(zhí)行
python demo.py
-> 打印出結(jié)果(win, mac)直接執(zhí)行.py 文件: cd 到文件目錄 -> 終端中
chmod a+x demo.py
給與文件可執(zhí)行的能力 -> 終端中./demo.py
-> 打印結(jié)果(mac系統(tǒng))直接執(zhí)行.py文件: 終端中
demo.py
-> 即會執(zhí)行(win系統(tǒng))
-
交互模式 和 直接執(zhí)行.py 文件的區(qū)別
交互模式: 啟動 Python 解釋器, 輸入一行解釋一行, 不做保存(適合單端代碼的測試)
.py 文件: 一次性執(zhí)行完畢, 不給打斷
-
print()輸出
逗號以空格輸出:
print('hello', 'world') -> 逗號以空格輸出 -> hello world
自動計(jì)算:
print(300 + 300) -> 600(可以做計(jì)算)
print('300 + 100 =', 300 + 100) -> 300 + 100 = 600
-
input 和 變量
input 默認(rèn)輸出 str, 所以如果需要轉(zhuǎn)化成整數(shù)需要用到
int(input())
當(dāng)
int('abc')
, 發(fā)現(xiàn) abc 不能轉(zhuǎn)化成整數(shù)的時(shí)候, 就會報(bào)ValueError
的錯name = input()
回車 ->>>>
等待輸入值 -> name 存儲你輸入的值 ->>>>name
-> 打印輸入值
Python 代碼運(yùn)行助手
作用: 讓你在線輸入 Python 代碼镐作,然后通過本機(jī)運(yùn)行的一個
Python腳本(就是learning.py)
來執(zhí)行代碼, 直接在網(wǎng)頁輸出結(jié)果終端執(zhí)行
python learning.py
-> 終端輸出Ready for Python code on port 39093...
等代表運(yùn)行成功打開
https://local.liaoxuefeng.com:39093/
, 就是 Python 的在線編輯器, 點(diǎn)擊 run 即可輸出結(jié)果網(wǎng)頁 run 不輸出結(jié)果的話,
Ctrl + c
中斷, 再執(zhí)行步驟 2
基本語法
基礎(chǔ)
-
基礎(chǔ)
-
#
: 注釋 -
:
: 當(dāng)語句以冒號:結(jié)尾時(shí)票灰,縮進(jìn)的語句視為代碼塊(相當(dāng)于js的大括號) - Python 大小寫敏感
-
-
數(shù)據(jù)類型和變量
- 整數(shù): 十進(jìn)制, 二進(jìn)制, 十六進(jìn)制
- 浮點(diǎn)數(shù): 小數(shù)
- 字符串:
'
,"
- 轉(zhuǎn)義符:
'I\'m \"OK\"!'
=>I'm "OK"!
,\n
表示換行派任,\t
表示制表符违柏,字符\本身也要轉(zhuǎn)義为肮,所以\\
表示的字符就是\ - 不轉(zhuǎn)義
r''
,''
內(nèi)部字符串不轉(zhuǎn)義, 直接輸出 - 換行
'''...'''
, 多行時(shí), 比\n
來的方便# 交互命令行內(nèi)... 是提示符, 并不需要手動輸入 print('''line1 ... line2 ... lin3''') # .py文件中 print('''line1 line2 line3''') # 還可以 print(r'''line1 # line2 # line3''')
- 布爾值(True, False) 和 布爾運(yùn)算
# 布爾值, 注意大小寫 >>> False False # 布爾運(yùn)算 >>> 3 > 2 True # and帐萎、or和not運(yùn)算 >>> True and False False >>> True or False True >>> not 1 > 2 True
- 空值:
None
- 變量: 變量名必須是大小寫英文、數(shù)字和_的組合敞曹,且不能用數(shù)字開頭
- 常量: 通常用全部的大寫
- 除法:
/
, 得到的都是浮點(diǎn)數(shù),>>> 9/3
->3.0
- 地板除:
//
, 只取結(jié)果的整數(shù)部分 - 取余數(shù):
%
總結(jié): 萬物皆對象, Python 對整數(shù)無大小限制,對浮點(diǎn)數(shù)超出一定范圍直接顯示
inf
(無限大)
字符串編碼
ord()
, 函數(shù)獲取字符的整數(shù)表示, 英文轉(zhuǎn) ascii 碼的數(shù)字chr()
, 函數(shù)把編碼轉(zhuǎn)換為對應(yīng)的字符, 相反b'ABC'
, 把 Python 的 str 轉(zhuǎn)成以字節(jié)為單位的 bytes,ABC
和b'abc'
, 一個是 str, 一個是 bytes, 每個字符只占一個字節(jié)-
str
通過encode()
方法可以編碼為指定的 bytes>>> 'ABC'.encode('ascii') b'ABC' >>> '中文'.encode('utf-8') b'\xe4\xb8\xad\xe6\x96\x87' # 中文可以轉(zhuǎn)成utf-8, 但是不可以轉(zhuǎn)成ascii, 因?yàn)橹形某龇秶? # 在bytes中账月,無法顯示為ASCII字符的字節(jié),用\x##顯示澳迫。 >>> '中文'.encode('ascii') Traceback (most recent call last): File "<stdin>", line 1, in <module> UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
-
bytes
通過decode
方法, 轉(zhuǎn)成 str>>> b'ABC'.decode('ascii') 'ABC' >>> b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8') '中文' # 如果bytes中包含無法解碼的字節(jié)局齿,decode()方法會報(bào)錯 # 如果bytes中只有一小部分無效的字節(jié),可以傳入errors='ignore'忽略錯誤的字節(jié) >>> b'\xe4\xb8\xad\xff'.decode('utf-8', errors='ignore') '中'
-
len()
函數(shù)計(jì)算的是 str 的字符數(shù)橄登,如果換成 bytes抓歼,len()函數(shù)就計(jì)算字節(jié)數(shù)>>> len(b'ABC') 3 >>> len(b'\xe4\xb8\xad\xe6\x96\x87') 6 >>> len('中文'.encode('utf-8')) 6
-
str 和 bytes 的互相轉(zhuǎn)換。為了避免亂碼問題拢锹,應(yīng)當(dāng)使用 UTF-8 編碼對 str 和 bytes 進(jìn)行轉(zhuǎn)換
# 為了告訴Linux/OS X系統(tǒng)谣妻,這是一個Python可執(zhí)行程序,Windows系統(tǒng)會忽略這個注釋卒稳; # 告訴Python解釋器蹋半,按照UTF-8編碼讀取源代碼,否則充坑,你在源代碼中寫的中文輸出可能會有亂碼减江。 #!/usr/bin/env python3 # -*- coding: utf-8 -*-
-
格式化(有點(diǎn)像 js 的模板字符串)
# %s:字符串, %d:整數(shù), %f: 浮點(diǎn)數(shù), %x:十六進(jìn)制整數(shù) # 需要替換的地方依次填寫參數(shù) # %% 代表轉(zhuǎn)義 % >>> 'hello, %s, you have $%d' % ('bin.wang', 100) 'hello, bin.wang, you have $100'
計(jì)算機(jī)儲存使用 Unicode染突,只有在傳輸?shù)臅r(shí)候才轉(zhuǎn)換成 UTF-8
-
str 不可變性
>>> a = 'abc' # replace方法 >>> a.replace('a', 'A') # 變的是變量a, 字符串'abc'并沒有變 >>> a 'Abc'
list 和 tuple
-
list(類比 js 數(shù)組)
>>> list = ['one', 'two', 'three'] # 求得list長度 >>> len(list) 3 # list下標(biāo)代表具體某個值 >>> list[len(list) - 1] 'three' # -1 代表最后一個, -2代表倒數(shù)第二個 >>> list[-1] # 正向反向越界都報(bào) `IndexError` 的錯
-
list 的方法
append()
: list.appen('test'), 末尾追加pop()
: list.pop(), 刪除末尾, list.pop(1), 刪除下標(biāo)為 1 的那個insert
: list.insert(1, 'test'), 在索引 1 的地方插入sort()
: list.sort(), 根據(jù) ascii 大小排序
-
tuple(元組)
-
跟 list 類似, 但是 tuple 一旦初始化就不能修改, 取得方式一致, 但是沒有 list 的方法
>>> tuple = (1, 2, 3) >>> tuple[0] 1 # 注意當(dāng)python只有一個元素的元組時(shí)候, 必須帶上逗號, 以免識別成數(shù)學(xué)的括號運(yùn)算 >>> t = (1, )
-
關(guān)于 tuple 的不可變
# 元組指向list是一直不變的, 只是list可變 >>> t = ('a', 'b', ['c', 'd']) >>> t[2][0] = 'X' >>> t[2][1] = 'Y' >>> t ('a', 'b', ['X', 'Y'])
-
條件判斷
-
條件判斷
age = 20 # 每個條件判斷以 : 分割, elif 是 else if的縮寫 if age >= 18: print('成年人') elif age >= 16: print('青少年') else: print('少年')
循環(huán)
-
for...in
循環(huán)(list, tuple, dict都可使用)name = ['Bob', 'Jack'] # Python 以 : 代替JS的 {} for key in name: print(key)
range()
函數(shù),可以生成一個整數(shù)序列-
list()
函數(shù), 可以轉(zhuǎn)換為 list# range 生成從0開始小于5的整數(shù), list把他們合成數(shù)組 >>> list(range(5)) [0, 1, 2, 3, 4]
-
while
循環(huán), 只要條件滿足辈灼,就不斷循環(huán)num = 5 sum = 0 while num > 0: sum = sum + num num = num - 1 print(sum)
break
, 可提前結(jié)束循環(huán)continue
語句跳過某些循環(huán)
dict 和 set
-
dict, 官網(wǎng)說像 map, 個人覺得像對象
>>> d = {'Nacy': 83, 'Bob': 93} >>> d['Bob'] 93 # 多次賦值, 會覆蓋 >>> d['Bob'] = 98 >>> d['Bob'] 98
-
dict 的 key 值
key 值不存在, 會報(bào)錯
KeyError
-
判斷 key 值存在
b = {'Bob': 123} # 第一種in >>> 'test' in b False # 第二種 get()方法,返回None 在交互模式下None不顯示 >>> b.get('test') # 不顯示 >>> b.get('Bob') 123 >>> b.get('test', -1) -1 >>> b.get('Bob', -1) 123
-
dict 和 list 的區(qū)別
- dict: key 找 value, 根據(jù) key 值算出(哈希算法)'頁碼', 再根據(jù)頁碼查找 value, 所以占用內(nèi)存較多
- list: 下標(biāo)找 value, 查找是從頭到尾, 直到找到, list 越長, 耗時(shí)慢
-
刪除一個 key,
pop()
方法, 對應(yīng)的 value 也會被刪除相反, 新增的話直接是
d['Bob'] = '123'
, 即可
-
set
-
傳入 list 作為輸入集合, 重復(fù)元素自動被過濾, 只存儲 key, 不存 value, 且無序
>>> s = set([1, 1, 2, 2, 3, 3]) >>> s {1, 2, 3} # 通過add(key)添加新key, 重復(fù)添加沒效果 >>> s.add(4) >>> s {1, 2, 3, 4} # 通過remove(key)方法可以刪除元素 >>> s.remove(1) >>> s {2, 3, 4}
-
set 可以看成無序和無重復(fù)元素的集合, 因此份企,兩個 set 可以做數(shù)學(xué)意義上的交集、并集等操作
>>> s1 = set([1, 2, 3]) >>> s2 = set([2, 3, 4]) >>> s1 & s2 {2, 3} >>> s1 | s2 {1, 2, 3, 4}
-
函數(shù)
內(nèi)置函數(shù)
參考地址:
https://docs.python.org/3/library/functions.html
求絕對值函數(shù): abs(num)
abs(-100) -> 100
求最大值: max(num1, num2)
max(1, 2, -3) -> 2
-
類型轉(zhuǎn)換, 交互模式下, help(hex)查看 hex 函數(shù)作用
>>> int('123') 123 >>> int(12.84) 12 >>> int('abc') # int第二個參數(shù)base是代表進(jìn)制, 第一個參數(shù)是16進(jìn)制, 轉(zhuǎn)成十進(jìn)制, 16^1 + 6 = 22 >>> int('16', base=16) 22 >>> int('123', 8) 83 # 報(bào)錯 ValueError >>> float('12.34') 12.34 >>> str(1.23) '1.23' >>> str(100) '100' >>> bool(1) True >>> bool('') False
自定義函數(shù)
-
def
定義函數(shù)# 在交互模式下直接敲出函數(shù) # 沒有定義return的, 最后還是會return None def my_abs(x): if x >= 0: return x else: return -x # 在交互模式下 import 引入函數(shù) >>> from demo(文件名不帶.py) import my_abs(函數(shù)名) >>> my_abs(-10) 10
-
定義空函數(shù)
def nop(): # pass相當(dāng)于占位符,代表不干啥事, 不填寫會報(bào)錯 pass
-
檢查參數(shù)
def my_abs(x): # isinstance判斷參數(shù)類型是不是整數(shù)或者浮點(diǎn)數(shù), 不是就raise 一個錯誤 if not isinstance(x, (int, float)): raise TypeError('參數(shù)錯誤') if x >= 0: return x else: return -x
-
返回多個值
>>> def Fn(x, y): return x, y >>> r = Fn('abc', 'cba') >>> print(r) # 返回多個值其實(shí)返回的是一個tuple, 返回一個tuple可以省略括號 ('abc', 'cba')
函數(shù)的參數(shù)(挺繞的, 多練習(xí))
-
位置參數(shù)(必選參數(shù))
# x必須且依次, 即位置參數(shù) def sum (x): return x * x
-
默認(rèn)參數(shù)
概念: 默認(rèn)參數(shù)必須要用不可變對象
#必選參數(shù)在前, 默認(rèn)參數(shù)在后 def sum (x, n = 2, age = 18) pass # n還是使用默認(rèn)值 sum(2, age = 16) # 默認(rèn)參數(shù)的坑, 默認(rèn)參數(shù)必須指向不變對象巡莹! def add_end(L = []): L.append('END') return L # 調(diào)用三次, 會一直加, 原因在: Python函數(shù)在定義的時(shí)候司志,默認(rèn)參數(shù)L的值就被計(jì)算出來了,即[]降宅,因?yàn)槟J(rèn)參數(shù)L也是一個變量俐芯,它指向?qū)ο骩],每次調(diào)用該函數(shù)钉鸯,如果改變了L的內(nèi)容吧史,則下次調(diào)用時(shí),默認(rèn)參數(shù)的內(nèi)容就變了唠雕,不再是函數(shù)定義時(shí)的[]了贸营。 add_end() ['END', 'END', 'END'] # 修改 def add_end(L = None): if L is None: L = [] L.append('END') return L
-
可變參數(shù)
概念: 允許傳入0個或若干個參數(shù), 定義時(shí)參數(shù)帶
*
# 定義一個a^2 + b^2...的函數(shù) def getSum(*number): sum = 0 for key in number: sum = sum + key * key return sum tuple = (3, 4) # 直接傳參, 調(diào)用時(shí)自動組裝成tuple getsum(1, 2) # 直接拿tuple使用. 記得帶星號 getSum(*tuple)
-
關(guān)鍵字參數(shù)
概念: 允許傳入0個或若干個含參數(shù)名的參數(shù), 調(diào)用時(shí)自動組成dict, 定義和調(diào)用都需要使用
**
def person(name, age, **kw): print('name: %s, age: %d, other: %s' % (name, age, kw)) dict = {'city': 'sz'} # kw得到的是dict的拷貝 person('bin.wang', 18, **dict)
-
命名關(guān)鍵字參數(shù)
概念: 為限制關(guān)鍵字參數(shù)名字, key = value, 且不是dict
# 以*為分割, 后面的為命名關(guān)鍵字參數(shù) def person(name, *, city = 'SZ', age) print(name, city, age) person('test', age = 18) # 傳參時(shí), 必須傳入?yún)?shù)名和參數(shù)值, 否則相當(dāng)于多傳了位置參數(shù), 報(bào)錯 person('test', 18) # 命名關(guān)鍵字參數(shù)可以由默認(rèn)值, city
-
參數(shù)組合
概念: 參數(shù)定義的順序必須是:必選參數(shù)、默認(rèn)參數(shù)岩睁、可變參數(shù)钞脂、命名關(guān)鍵字參數(shù)和關(guān)鍵字參數(shù)
def f1(a, b, c=0, *args, **kw): >>> f1(1, 2, 3, 'a', 'b') a = 1 b = 2 c = 3 args = ('a', 'b') kw = {} # 通過tuple 和 dict的話, 可簡單到 >>> args = (1, 2, 3, 4) >>> kw = {'d': 99, 'x': '#'} >>> f1(*args, **kw) a = 1 b = 2 c = 3 args = (4,) kw = {'d': 99, 'x': '#'} # 必選, 默認(rèn), 命名關(guān)鍵字參數(shù), 關(guān)鍵字參數(shù) def f2(a, b, c=0, *, d, **kw): >>> f2(1, 2, d=99, ext=None) a = 1 b = 2 c = 0 d = 99 kw = {'ext': None}
遞歸函數(shù)
函數(shù)內(nèi)部, 調(diào)用本身, 過深的調(diào)用可能導(dǎo)致棧溢出
棧溢出科普: 計(jì)算機(jī)中函數(shù)的調(diào)用通過棧數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)的, 每進(jìn)一個函數(shù), 棧就加一層棧幀, return一次就減少一層棧幀, 當(dāng)函數(shù)調(diào)用過多, 即會棧溢出
-
python棧溢出報(bào)錯:
RecursionError: maximum recursion depth exceeded in comparison
def getFn(n): if n == 1: return 1 return n * getFn(n - 1)
例子: 移動漢諾塔
高級特性
切片(slice)
-
對list, tuple, str的數(shù)據(jù)處理
# 對list或者tuple L = [] n = 0 while n <= 99: L.append(n) n = n + 1 # 生成L是[0-99]的list # slice切片, 取索引0到索引3(不包括3), 即0, 1, 2 >>> L[ : 3] >>> [0, 1, 2] # L[-1]代表最后一位, 那么包括倒數(shù)第三位, 不包括倒數(shù)第一位 >>> L[-3: -1] >>> [97, 98] # 前十位, 每隔5位取一個 >>> L[ : 10 : 5] >>> [0, 5] # 所有l(wèi)ist, 隔20取一位 >>> L[ : : 20] >>> [0, 20, 40, 60, 80] # 完全復(fù)制一個list >>> n = L[ : ]
>>> str = 'mytest' # 每隔2位取一位 >>> str[ : : 2] >>> 'mts'
迭代
for...in...
遍歷也叫迭代, list, tuple, dict, str, generator都可迭代-
迭代的用法
-
list 和 tuple的迭代
# 如果迭代的時(shí)候想把下標(biāo)輸出 # 引用python內(nèi)置的enumerate函數(shù) >>> list = [4, 5, 6] >>> for index, value in enmurate(list): >>> 0 4 1 5 2 6 # 常見多變量 >>> for x, y in [(1, 1), (2, 4)]: ... print(x, y) >>> 1 1 2 4
-
dict的迭代
>>> dict = {'Bob': 18, 'Nancy': 17} # 獲取key值 >>> for key in dict: >>> 'Bob' 'Nancy' # 獲取value >>> for value in dict.values(): # 獲取key和value值 >>> for key, value in dict.items(): >>> Bob 18 Nancy 17
-
str的迭代
>>> str = 'test' >>> for key in str: >>> t e s t
-
-
檢測可迭代對象(區(qū)別于Iterator, 迭代器)
- 概念: 只要是可迭代對象, for循環(huán)就不會報(bào)錯, 通過collections模塊的Iterable類型判斷可迭代對象
>>> from collections import Iterable >>> isinstance([1,2,3], Iterable) >>> True >>> isinstance('str', Iterable) >>> True >>> isinstance(123, Iterable) >>> False
列表生成式
- 概念: Python內(nèi)置的用來創(chuàng)建list的生成式
# list部分 >>> list(range(1, 6)) >>> [1, 2, 3, 4, 5] # 生成 [1 * 1, 2 * 2, 3 * 3] >>> [x * x for x in range(1, 4)] >>> [1, 4, 9] # 還可以進(jìn)行篩選 >>> [x * x for x in range(1, 4) if x % 2 == 0] >>> [4] # 使用兩層循環(huán),可以生成全排列, 即排列組合 >>> [m + n for m in 'ABC' for n in 'XYZ'] ['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ'] # 把list中所有的字符串變成小寫 >>> list = ['Hello', 'World'] >>> [s.lower() for s in list] >>> ['hello', 'world] # dict部分 >>> dict = {'x': 'A', 'y': 'B'} >>> [k + '=' + v for k, v in dict.items()] >>> ['x=A', 'y=B']
生成器(generator)
概念通過列表生成式, 直接生成的list, 當(dāng)量大時(shí)耗內(nèi)存, 使用生成器按照邏輯邊循環(huán)邊計(jì)算, 節(jié)省空間
-
簡單方式
# 直接把列表生成式改成 簡單的generator 區(qū)別: [] => () >>> g = (x * x for x in [1, 2]) >>> <generator object <genexpr> at 0x1022ef630>
-
遍歷每一項(xiàng)
# 每次調(diào)用next(g)捕儒,就計(jì)算出g的下一個元素的值冰啃,直到計(jì)算到最后一個元素,沒有更多的元素時(shí)刘莹,拋出StopIteration的錯誤 >>> next(g) >>> 1 >>> next(g) >>> 4 # 沒有了就結(jié)束 >>> next(g) >>> StopIteration # 通過 for...in...(常用) >>> for n in g >>> 1 4
-
斐波那契
# 相當(dāng)于 t = (0, 0, 1) => a = t[0] >>> n, a, b = 0, 0, 1 # 關(guān)鍵字yield, 生成generator def fib(max): n, a, b = 0, 0, 1 while n < max: yield b a, b = b, a + b n = n + 1 return 'done' # generator和函數(shù)的執(zhí)行流程不一樣, 函數(shù)是順序執(zhí)行阎毅,遇到return語句或者最后一行函數(shù)語句就返回, 變成generator的函數(shù),在每次調(diào)用next()的時(shí)候執(zhí)行点弯,遇到y(tǒng)ield語句返回扇调,再次執(zhí)行時(shí)從上次返回的yield語句處繼續(xù)執(zhí)行。
迭代器
概念: 可以被next()函數(shù)調(diào)用并不斷返回下一個值的對象稱為迭代器:Iterator, 生成器都是Iterator對象,
但list抢肛、dict狼钮、str雖然是Iterable(可迭代),卻不是Iterator(可迭代對象)
-
dict, list, tuple, str數(shù)據(jù)類型為何不是Iterator ?
Iterator對象表示的是一個
數(shù)據(jù)流
, 被next()函數(shù)調(diào)用時(shí)不斷返回下一個數(shù)據(jù), 直到?jīng)]有數(shù)據(jù)時(shí)拋出StopIteration
錯誤, 我們不能提前知道序列長度, 所以Iterator的計(jì)算是惰性的捡絮,只有在需要返回下一個數(shù)據(jù)時(shí)它才會計(jì)算.Iterator甚至可以表示一個無限大的數(shù)據(jù)流
熬芜,例如全體自然數(shù)。而使用list是永遠(yuǎn)不可能存儲全體自然數(shù)的. -
判斷是否Iterator對象
>>> from collections import Iterator >>> isinstance([], Iterator) # 不是迭代器 >>> False
-
使用
iter()函數(shù)
變成迭代器Iterator# 使用內(nèi)置iter()函數(shù), list福稳、dict涎拉、str等Iterable變成Iterator >>> isinstance(iter([]), Iterator) >>> True
-
小結(jié):
~ 凡是可作用于for循環(huán)的對象都是Iterable類型;
~ 凡是可作用于next()函數(shù)的對象都是Iterator類型,它們表示一個惰性計(jì)算的序列曼库;
~ 集合數(shù)據(jù)類型如list区岗、dict略板、str等是Iterable但不是Iterator毁枯,不過可以通過iter()函數(shù)獲得一個Iterator對象。
~ for循環(huán)本質(zhì)上就是通過不斷調(diào)用next()函數(shù)實(shí)現(xiàn)的
it = iter([1, 2, 3]) while True: try: x = next(it) print(x) except StopIteration: print('done') break
函數(shù)式編程
- 由于python允許使用變量, 故而python不是純函數(shù)式(固定輸入固定輸出)編程語言
高階函數(shù)
函數(shù)名也是變量, 只是函數(shù)名已經(jīng)指定到函數(shù)
-
概念: 一個函數(shù)接受另外一個函數(shù)作為參數(shù), 高階函數(shù)
>>> def add(x, y, f): ... return f(x) + f(y) >>> add(-5, 1, abs) >>> 6
map/reducer
-
map: 接受兩個參數(shù),
函數(shù)
和Iterable
,map將傳入的函數(shù)依次作用到序列的每個元素
叮称,結(jié)果返回新的Iterator
>>> def Fn(x): ... return x * x # 第一個參數(shù)是函數(shù), 第二個參數(shù)是Iterable >>> map(Fn, [1, 2, 3]) # 直接輸出的是map對象 <map object at 0x000001B9AF26F4E0> # Iterator是惰性序列种玛,通過list()函數(shù)讓它把整個序列都計(jì)算出來并返回一個list >>> print(list(map(Fn, [1, 2, 3]))) [1, 4, 9]
-
reduce: 接受
函數(shù)
和Iterable
, 這個函數(shù)必須接收兩個參數(shù),reduce
把結(jié)果繼續(xù)和序列的下一個元素做累積計(jì)算, 返回結(jié)果不是Iterator- 效果就是:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
>>> from functools import reduce >>> def add(x, y): ... return x * 10 + y ... >>> reduce(add, [1, 2, 3]) 123
- 效果就是:
-
自己實(shí)現(xiàn)原生的
int
函數(shù)from functools import reduce num = {'1': 1, '2': 2, '3'} def str2int(s): def fn(x, y): return x * 10 + y def char2num(x): return num[x] return reduce(fn, map(char2num, s)) # 借用map和reduce, 此時(shí)輸入'123', 調(diào)用str2int函數(shù), 會轉(zhuǎn)化成數(shù)字123
filter
-
概念: 內(nèi)置高階函數(shù), 用于過濾序列, 返回
Iterator
def no_empty(s): return s and s.strip() l = filter(no_empty, ['a', '', None, 'B']) # 返回的是惰性序列, 使用list()獲得結(jié)果, 并返回list list(l)
埃氏篩法: 求解素?cái)?shù), 2素?cái)?shù), 2倍數(shù)刪除, 3素?cái)?shù), 3的倍數(shù)刪除, 一次下去, 求得所有素?cái)?shù)
sorted
-
概念: 內(nèi)置高階函數(shù), 對list進(jìn)行排序, 可以接收一個key函數(shù)來實(shí)現(xiàn)自定義的排序, 返回排序之后的list
# 基本用法 >>> sorted([10, -10, 2, 5]) [-10, 2, 5, 10] # 作為高階函數(shù)用法, 按key作用后的結(jié)果排序 sorted([5, -4, 3], key=abs) [3, -4, 5]
-
字符串排序, 按
ASCII
碼進(jìn)行排序# ascii碼里面, b > A >>> sorted(['bob', 'Amazing', 'Test']) ['Amazing', 'Test', 'bob'] # 全部轉(zhuǎn)化為小寫再比較 >>> sorted(['bob', 'Amazing', 'Test'], key=str.lower) ['Amazing', 'bob', 'Test']
反向排序, 可以傳入第三個參數(shù)
reverse=True
返回函數(shù)
-
例子
# 類似js閉包, 內(nèi)部函數(shù)可以得到外部函數(shù)的局部變量 def lazy_sum(*num): def get_sum(): sum = 0 for key in num: sum = sum + key return sum return get_sum # 每次調(diào)用都是返回新的函數(shù) f = lazy_sum(1, 2, 3) # 再次調(diào)用才得出結(jié)果 f()
閉包
返回閉包時(shí)牢記一點(diǎn):返回函數(shù)不要引用任何循環(huán)變量,或者后續(xù)會發(fā)生變化的變量瓤檐。
```py
# 所引用的變量i全部變成了3
def count():
l = []
for i in range(1, 4):
def fn():
return i * i
l.append(fn)
return l
f1, f2, f3 = count()
>>> f1()
9
>>> f2()
9
>>> f3()
9
# 改進(jìn)
def count():
def f(j):
def g():
return j*j
return g
fs = []
for i in range(1, 4):
fs.append(f(i)) # f(i)立刻被執(zhí)行赂韵,因此i的當(dāng)前值被傳入f()
return fs
```
匿名函數(shù)
-
概念: 關(guān)鍵字
lambda
表示匿名函數(shù),冒號前面的x表示函數(shù)參數(shù), 只能有一個表達(dá)式, 表達(dá)式結(jié)果就是return的返回值>>> f = lambda x: x * x # 等價(jià) >>> def f(x): ... return x * x
裝飾器(decorator)
概念: 在代碼運(yùn)行期間動態(tài)增加功能, 本質(zhì)上是返回函數(shù)的高階函數(shù)
-
__name__屬性
挠蛉,可以拿到函數(shù)的名字def fn(x): return x f = fn # 通過__name__拿到函數(shù)名字 >>> fn.__name__ 'fn' >>> f.__name__ 'fn'
-
例子
# 不帶參數(shù)的decorator import functools def log(func): # 為了最終返回的decorator的__name__指向的還是func而不會變成wrapper @functools.wraps(func) def wrapper(*args, **kw): print('call %s():' % func.__name__) return func(*args, **kw) return wrapper # 帶參數(shù)的decorator import functools # 帶參數(shù)的decorator, 在原來函數(shù)中可以做其他的操作 def log(text): def decorator(func): @functools.wraps(func) def wrapper(*args, **kw): print('%s %s():' % (text, func.__name__)) return func(*args, **kw) return wrapper return decorator # 調(diào)用 def fn(x): print('調(diào)用', x) fn = log('log日志')(fn) fn(88)
偏函數(shù)
-
概念: 偏函數(shù)是由functools模塊提供,
functools.partial
把一個函數(shù)的某些參數(shù)固定住(設(shè)置默認(rèn)值), 返回新的函數(shù)>>> import functools >>> int2 = functools.partial(int, base = 16) # 預(yù)設(shè)有10, 如果都不比是10大就選擇10 >>> max2 = functools.partial(max, 10) >>> max2(1, 2, 3) 10
模塊
概念: 一個.py文件一個模塊, 跨模塊不會出現(xiàn)變量名沖突
-
包: 為避免模塊名沖突, 每個包下面都有
__init__.py
文件# 引入模塊, myCompany是頂層包, a是a.py模塊, test是a.py的方法 import myCompany.a myCompany.a.test()
自己創(chuàng)建模塊時(shí)要注意命名祭示,不能和Python自帶的模塊名稱沖突
[圖片上傳失敗...(image-f76f68-1547169230449)]
使用模塊
-
例子
# 任何模塊代碼的第一個字符串都被視為模塊的文檔注釋; 'my first module' # 代碼作者 __author__ = 'bin.wang' import sys def test(): args = sys.argv print(args) if len(args) == 1: print('hello world') elif len(args) == 2: print('hello, %s' % args[1]) else: print('too many arguments') # 當(dāng)運(yùn)行到這個模塊時(shí), __name__會更改為__main__, 當(dāng)導(dǎo)入時(shí)判斷語句為False # 這是常見的運(yùn)行測試 if __name__ == '__main__': test()
-
作用域
- 變量的命名規(guī)則
模塊內(nèi)部使用:_XXX
和__XXX
公開的函數(shù):XXX
特殊變量:__XXX__
, 直接引用但一般特殊用途,__name__
- 變量的命名規(guī)則
第三方模塊(類比js的npm)
安裝第三方模塊, 包管理工具pip
-
安裝pip
win下安裝: 安裝python時(shí), 勾選
pip和Add python.exe to Path
(但我是勾選了Add Python 3.7 to PATH
)mac系統(tǒng)下安裝: 記得使用pip3
pip安裝第三方模塊: 一個一個安裝費(fèi)時(shí), 且需要考慮兼容性
-
安裝Anaconda(需要研究)
概念: 基于Python的數(shù)據(jù)處理和科學(xué)計(jì)算平臺, 內(nèi)置許多第三方庫, MySQL驅(qū)動程序谴古,Web框架Flask质涛,科學(xué)計(jì)算Numpy等.
Anaconda會把系統(tǒng)Path中的python指向自己自帶的Python,并且掰担,Anaconda安裝的第三方模塊會安裝在Anaconda自己的路徑下汇陆,不影響系統(tǒng)已安裝的Python目錄
地址:
https://www.anaconda.com/download/
面向?qū)ο缶幊?/h1>
- 概念: OOP, 設(shè)計(jì)思想, 封裝繼承和多態(tài)
類和實(shí)例
-
類和實(shí)例: 類實(shí)例化出對象, 對象之間方法一致, 數(shù)據(jù)不一樣
# 關(guān)鍵字class, 類名大寫, object類, 代表該類是從object類繼承下來
class Student(object):
# 在創(chuàng)建實(shí)例時(shí)就把, name和score綁定上去, 類比jsconstruct
# self代表實(shí)例本身(類比js的this), 創(chuàng)建實(shí)例就需要傳入name和score
def __init__(self, name, score):
# 加上__, 變成私有變量, 外部就不能訪問
self.__name = name
self.__score = score
def print_score(self):
print('%s: %s' % (self.__name, self.score))
# 調(diào)用, 創(chuàng)建實(shí)例, 調(diào)用方法
ben = Student('ben', 99)
ben.print_score()
訪問限制
- 概念: 在屬性名稱前加上
__
, 變成私有變量, 這樣外部就不能訪問了(_XX
還能訪問, 但也約定是私有變量), 注意區(qū)分__XXX__
, 特殊變量, 能直接訪問
繼承和多態(tài)
```py
class Animal(object):
def run(self):
print('animal is running')
# 繼承, 子類方法覆蓋父類同類方法
class Cat(Animal):
def run(self):
print('cat is running')
# 實(shí)例化的對象跟str, list等一樣都屬于某個數(shù)據(jù)類型, 所以也能做類型判斷
cat = Cat()
isinstance(cat, Cat) # True
isinstance([], list) # True
```
獲取對象信息(判斷對象類型)
type()函數(shù): type(123)
-> <class 'int'>
isinstance()
-
dir(): 獲得一個對象的所有屬性和方法, 返回一個包含字符串的list
>>> dir(list)
['__add__', '__len__',..., 'copy']
# 調(diào)用python內(nèi)置的len(list), 其實(shí)就是調(diào)用了__len__方法, 跟list.__len__() 是一樣的
-
getattr(), hasattr(), setattr()
class MyObject(object):
def __init__(self):
self.x = 9
def power(self):
return self.x * self.x
obj = MyObject()
hasattr(obj, 'x') # True
hasattr(obj, 'power') # True
setattr(obj, 'y', 2)
hasattr(obj, 'y') # True
實(shí)例屬性和類屬性
- 給實(shí)例綁定屬性, 方法, 給類綁定屬性, 方法
from types import MethodType
# 定義類
class Student(object):
# 給類加屬性
gender = '男'
def __init__(self, name):
self.name = name
# 類綁定方法, 實(shí)例化的對象都可以使用
def setScore(self, score):
self.score = score
Student.setScore = setScore
# 實(shí)例化, 綁定的屬性可方法, 只適用于本實(shí)例
s = Student('Ben')
# 實(shí)例綁定屬性, 優(yōu)先級大于類
s.gender = '女'
def getAge(self, age):
self.age = age
s.getAge = MethodType(getAge, s)
s.getAge(18)
s.age # 18, 且此實(shí)例不會影響別的實(shí)例
面向?qū)ο蟾呒壘幊?進(jìn)階版本)
使用slots
-
概念: 限制實(shí)例的屬性, 繼承它的子類不起作用, 但是子類設(shè)置了__slots__
, 則會把父類的slots一起繼承
class Student(object):
# 用tuple的方式限定實(shí)例可增加的屬性
__slots__ = ('name', 'age')
s = Student()
# AttributeError: 'Student' object has no attribute 'gender'
s.gender = '男'
使用@property(不太理解)
-
概念: @property裝飾器, 把一個方法變成屬性調(diào)用的
# score是可讀寫屬性, 那么是只讀屬性
class Student(object):
# property裝飾器, 方法變成屬性一樣使用
@property
def score(self):
return self._score
# score的設(shè)置, 像設(shè)置屬性一樣使用里面的方法
@score.setter
def score(self, val):
self._score = val
# 只設(shè)置了讀, 沒設(shè)置寫入, 就是只讀
@property
def name(self):
return self._name
# 實(shí)例
s = Student()
s.score = 60
print(s.score) # 60
多重繼承
-
概念: 多重繼承,一個子類就可以同時(shí)獲得多個父類的所有功能
class Animal(object):
pass
class Runable(object):
pass
# 狗繼承了兩個父類
class Dog(Animal, Runable):
pass
MixIn: 上述的繼承多個的設(shè)計(jì), 目的就是給一個類增加多個功能
定制類
-
__str__
: 使得輸出更易理解
class Student(object):
def __init__(self, name):
self.name = name
def __str__(self):
return 'Student object (name: %s)' % (self.name)
__repr__ = __str__
# 原本: <__main__.Student object at 0x00000244C4994CF8>
# 現(xiàn)在: Student object (name: test)
print(Student('test'))
-
__iter__
: 使得類返回迭代對象, 使得類可迭代(不太理解作用)
class Fib(object):
def __init__(self):
self.a, self.b = 0, 1
def __iter__(self):
return self
def __next__(self):
self.a, self.b = self.b , self.a + self.b
# 退出循環(huán)條件
if self.a > 10000:
raise StopIteration
return self.a
for key in Fib():
print(key)
-
__getitem__
: 使得類除了forin迭代之外, 還可以根據(jù)下標(biāo)取值
class Fib(object):
def __getitem__(self, n):
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a
# 3, 能根據(jù)下標(biāo)取值
print(Fib()[3])
__getattr__
__call__
枚舉類
-
概念: Enum可以把一組相關(guān)常量定義在一個class中带饱,且class不可變毡代,成員可以直接比較
from enum import Enum, unique
# value屬性則是自動賦給成員的int常量,默認(rèn)從1開始計(jì)數(shù)
Month = Enum('Month', ('Jan', 'Feb'))
# 如果value的屬性想自定義, 則需要用到@unique裝飾器
@unique
class Weekday(Enum):
Sun = 0
Mon = 1
#訪問
for name, member in Weekday.__members__.items():
print(name, '=>', member, ',', member.value)
# Sun => Weekday.Sun , 0
# 根據(jù)value獲取值
print(Weekday.Sun.value) # 0
# 根據(jù)value也可以反推member
print(weekday(0)) # Sun
使用元類
- 概念
錯誤, 調(diào)試和測試
錯誤處理
try...except...finally...
-
try某步報(bào)錯立馬跳轉(zhuǎn)到except, 最后finally都會執(zhí)行
try:
print('try...')
r = 10 / int('2')
print('result:', r)
except ValueError as e:
print('值錯誤:', e)
except ZeroDivisionError as e:
print('0做分母:', e)
# try執(zhí)行不報(bào)錯的話, 才會執(zhí)行else
else:
print('no error!')
finally:
print('finally...')
print('END')
python 的錯誤也是class, 錯誤類型都繼承自BaseException
, 且捕獲該類型錯誤, 還會順便捕獲其子類錯誤
常見的錯誤類型和繼承關(guān)系: https://docs.python.org/3/library/exceptions.html#exception-hierarchy
調(diào)用棧
- 概念: 分析錯誤的調(diào)用棧信息勺疼,才能定位錯誤的位置教寂。
記錄錯誤
-
概念: 內(nèi)置的logging模塊可以記錄錯誤信息
import logging
def foo(s):
return 10 / int(s)
def bar(s):
return foo(s) * 2
def main():
# 直接通過logging捕獲錯誤, 但是不會退出程序的執(zhí)行, END還會執(zhí)行
try:
bar('0')
except Exception as e:
logging.exception(e)
main()
print('END')
拋出錯誤
- 概念
未完待續(xù)...
類和實(shí)例: 類實(shí)例化出對象, 對象之間方法一致, 數(shù)據(jù)不一樣
# 關(guān)鍵字class, 類名大寫, object類, 代表該類是從object類繼承下來
class Student(object):
# 在創(chuàng)建實(shí)例時(shí)就把, name和score綁定上去, 類比jsconstruct
# self代表實(shí)例本身(類比js的this), 創(chuàng)建實(shí)例就需要傳入name和score
def __init__(self, name, score):
# 加上__, 變成私有變量, 外部就不能訪問
self.__name = name
self.__score = score
def print_score(self):
print('%s: %s' % (self.__name, self.score))
# 調(diào)用, 創(chuàng)建實(shí)例, 調(diào)用方法
ben = Student('ben', 99)
ben.print_score()
__
, 變成私有變量, 這樣外部就不能訪問了(_XX
還能訪問, 但也約定是私有變量), 注意區(qū)分__XXX__
, 特殊變量, 能直接訪問```py
class Animal(object):
def run(self):
print('animal is running')
# 繼承, 子類方法覆蓋父類同類方法
class Cat(Animal):
def run(self):
print('cat is running')
# 實(shí)例化的對象跟str, list等一樣都屬于某個數(shù)據(jù)類型, 所以也能做類型判斷
cat = Cat()
isinstance(cat, Cat) # True
isinstance([], list) # True
```
type()函數(shù): type(123)
-> <class 'int'>
isinstance()
dir(): 獲得一個對象的所有屬性和方法, 返回一個包含字符串的list
>>> dir(list)
['__add__', '__len__',..., 'copy']
# 調(diào)用python內(nèi)置的len(list), 其實(shí)就是調(diào)用了__len__方法, 跟list.__len__() 是一樣的
getattr(), hasattr(), setattr()
class MyObject(object):
def __init__(self):
self.x = 9
def power(self):
return self.x * self.x
obj = MyObject()
hasattr(obj, 'x') # True
hasattr(obj, 'power') # True
setattr(obj, 'y', 2)
hasattr(obj, 'y') # True
from types import MethodType
# 定義類
class Student(object):
# 給類加屬性
gender = '男'
def __init__(self, name):
self.name = name
# 類綁定方法, 實(shí)例化的對象都可以使用
def setScore(self, score):
self.score = score
Student.setScore = setScore
# 實(shí)例化, 綁定的屬性可方法, 只適用于本實(shí)例
s = Student('Ben')
# 實(shí)例綁定屬性, 優(yōu)先級大于類
s.gender = '女'
def getAge(self, age):
self.age = age
s.getAge = MethodType(getAge, s)
s.getAge(18)
s.age # 18, 且此實(shí)例不會影響別的實(shí)例
概念: 限制實(shí)例的屬性, 繼承它的子類不起作用, 但是子類設(shè)置了__slots__
, 則會把父類的slots一起繼承
class Student(object):
# 用tuple的方式限定實(shí)例可增加的屬性
__slots__ = ('name', 'age')
s = Student()
# AttributeError: 'Student' object has no attribute 'gender'
s.gender = '男'
概念: @property裝飾器, 把一個方法變成屬性調(diào)用的
# score是可讀寫屬性, 那么是只讀屬性
class Student(object):
# property裝飾器, 方法變成屬性一樣使用
@property
def score(self):
return self._score
# score的設(shè)置, 像設(shè)置屬性一樣使用里面的方法
@score.setter
def score(self, val):
self._score = val
# 只設(shè)置了讀, 沒設(shè)置寫入, 就是只讀
@property
def name(self):
return self._name
# 實(shí)例
s = Student()
s.score = 60
print(s.score) # 60
概念: 多重繼承,一個子類就可以同時(shí)獲得多個父類的所有功能
class Animal(object):
pass
class Runable(object):
pass
# 狗繼承了兩個父類
class Dog(Animal, Runable):
pass
MixIn: 上述的繼承多個的設(shè)計(jì), 目的就是給一個類增加多個功能
__str__
: 使得輸出更易理解
class Student(object):
def __init__(self, name):
self.name = name
def __str__(self):
return 'Student object (name: %s)' % (self.name)
__repr__ = __str__
# 原本: <__main__.Student object at 0x00000244C4994CF8>
# 現(xiàn)在: Student object (name: test)
print(Student('test'))
__iter__
: 使得類返回迭代對象, 使得類可迭代(不太理解作用)
class Fib(object):
def __init__(self):
self.a, self.b = 0, 1
def __iter__(self):
return self
def __next__(self):
self.a, self.b = self.b , self.a + self.b
# 退出循環(huán)條件
if self.a > 10000:
raise StopIteration
return self.a
for key in Fib():
print(key)
__getitem__
: 使得類除了forin迭代之外, 還可以根據(jù)下標(biāo)取值
class Fib(object):
def __getitem__(self, n):
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a
# 3, 能根據(jù)下標(biāo)取值
print(Fib()[3])
__getattr__
__call__
概念: Enum可以把一組相關(guān)常量定義在一個class中带饱,且class不可變毡代,成員可以直接比較
from enum import Enum, unique
# value屬性則是自動賦給成員的int常量,默認(rèn)從1開始計(jì)數(shù)
Month = Enum('Month', ('Jan', 'Feb'))
# 如果value的屬性想自定義, 則需要用到@unique裝飾器
@unique
class Weekday(Enum):
Sun = 0
Mon = 1
#訪問
for name, member in Weekday.__members__.items():
print(name, '=>', member, ',', member.value)
# Sun => Weekday.Sun , 0
# 根據(jù)value獲取值
print(Weekday.Sun.value) # 0
# 根據(jù)value也可以反推member
print(weekday(0)) # Sun
try...except...finally...
try某步報(bào)錯立馬跳轉(zhuǎn)到except, 最后finally都會執(zhí)行
try:
print('try...')
r = 10 / int('2')
print('result:', r)
except ValueError as e:
print('值錯誤:', e)
except ZeroDivisionError as e:
print('0做分母:', e)
# try執(zhí)行不報(bào)錯的話, 才會執(zhí)行else
else:
print('no error!')
finally:
print('finally...')
print('END')
python 的錯誤也是class, 錯誤類型都繼承自BaseException
, 且捕獲該類型錯誤, 還會順便捕獲其子類錯誤
常見的錯誤類型和繼承關(guān)系: https://docs.python.org/3/library/exceptions.html#exception-hierarchy
概念: 內(nèi)置的logging模塊可以記錄錯誤信息
import logging
def foo(s):
return 10 / int(s)
def bar(s):
return foo(s) * 2
def main():
# 直接通過logging捕獲錯誤, 但是不會退出程序的執(zhí)行, END還會執(zhí)行
try:
bar('0')
except Exception as e:
logging.exception(e)
main()
print('END')
未完待續(xù)...