python實用案例
python 基礎知識
本文所有內容是學習期間做的筆記,僅為個人查閱和複習方便而記錄。所有內容均摘自:http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000 資料類型 整數 浮點數
字串 如果字串內部既包含'又包含",可以用逸出字元\來轉義。 多行字串可以通過'''字串內容'''來表示 r''表示''內部的字串預設不轉義 布爾值, true, false;布爾值可以用and、or和not運算 空值,None 變數, 變數名必須是大小寫英文、數字和_的組合,且不能用數字開頭
常量, 全部大寫的變數名表示常量
一種除法是//,稱為地板除,兩個整數的除法仍然是整數
>>> 10 // 33
list和tuple(元組)的區別
list定義,classmates = ['Michael', 'Bob', 'Tracy']
tuple定義,classmates = ('Michael', 'Bob', 'Tracy') list可變,tuple不變。 list用[]進行定義,tuple用()進行定義。都可以通過正整數和負數進行下標的擷取。
tuple所謂的“不變”是說,tuple的每個元素,`指向永遠不變`。即指向'a',就不能改成指向'b',指向一個list,就不能改成指向其他對象,但指向的這個list本身是可變的。
條件判斷
if <條件判斷1>: <執行1>elif <條件判斷2>: <執行2>elif <條件判斷3>: <執行3>else: <執行4>
注意條件判斷後面的:,不要少寫了。 迴圈
for...in迴圈
names = ['Michael', 'Bob', 'Tracy']for name in names:print(name)
range()函數,可以產生一個整數序列,再通過list()函數可以轉換為list
while迴圈
sum = 0n = 99while n > 0:sum = sum + nn = n - 2print(sum)
dict和set
dict:字典,類似於map,可重複,快速尋找。
>>> d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}>>> d['Michael']95
和list比較,dict有以下幾個特點: 尋找和插入的速度極快,不會隨著key的增加而變慢; 需要佔用大量的記憶體,記憶體浪費多。
而list相反: 尋找和插入的時間隨著元素的增加而增加; 佔用空間小,浪費記憶體很少。
所以,dict是用空間來換取時間的一種方法。
set:集合,不可重複。要建立一個set,需要提供一個list作為輸入集合
>>> s = set([1, 2, 3])>>> s{1, 2, 3}
函數
定義函數
定義一個函數要使用def語句,依次寫出函數名、括弧、括弧中的參數和冒號:,然後,在縮排塊中編寫函數體,函數的傳回值用return語句返回。
def my_abs(x): if x >= 0: return x else: return -x
注意,函數體內部的語句在執行時,一旦執行到return時,函數就執行完畢,並將結果返回。因此,函數內部通過條件判斷和迴圈可以實現非常複雜的邏輯。
如果沒有return語句,函數執行完畢後也會返回結果,只是結果為None。
return None可以簡寫為return。
空函數
def nop():pass
pass語句什麼都不做,
參數檢查
資料類型檢查可以用內建函數isinstance()實現
def my_abs(x):if not isinstance(x, (int, float)): raise TypeError('bad operand type')if x >= 0: return xelse: return -x
** 返回多個值 **
比如在遊戲中經常需要從一個點移動到另一個點,給出座標、位移和角度,就可以計算出新的新的座標:
import mathdef move(x, y, step, angle=0): nx = x + step * math.cos(angle) ny = y - step * math.sin(angle) return nx, ny
其實傳回值是一個tuple。但是,在文法上,返回一個tuple可以省略括弧,而多個變數可以同時接收一個tuple,按位置賦給對應的值,所以,Python的函數返回多值其實就是返回一個tuple,但寫起來更方便。 小結
定義函數時,需要確定函數名和參數個數;
如果有必要,可以先對參數的資料類型做檢查;
函數體內部可以用return隨時返回函數結果;
函數執行完畢也沒有return語句時,自動return None。
函數可以同時返回多個值,但其實就是一個tuple。 函數參數
預設參數
def power(x, n=2):s = 1while n > 0: n = n - 1 s = s * xreturn s
設定預設參數時,有幾點要注意: 一是必選參數在前,預設參數在後,否則Python的解譯器會報錯(思考一下為什麼預設參數不能放在必選參數前面); 二是如何設定預設參數。
當函數有多個參數時,把變化大的參數放前面,變化小的參數放後面。變化小的參數就可以作為預設參數。
使用預設參數有什麼好處。最大的好處是能降低調用函數的難度。
** 定義預設參數要牢記一點:預設參數必須指向不變對象。 如果是list,可用L=None來聲明 ** 可變參數
在參數前面加了一個*號即可。
定義:
def calc(*numbers): sum = 0 for n in numbers: sum = sum + n * n return sum
調用方式1:
>>> calc(1, 2)5>>> calc()0
調用方式2:可通過在list或tuple前面加一個*號,把list或tuple的元素變成可變參數傳進去
>>> nums = [1, 2, 3]>>> calc(*nums)14
關鍵字參數
定於:
def person(name, age, **kw):print('name:', name, 'age:', age, 'other:', kw)
調用方式1:
>>> person('Michael', 30)name: Michael age: 30 other: {}
調用方式2:
>>> person('Bob', 35, city='Beijing')name: Bob age: 35 other: {'city': 'Beijing'}>>> person('Adam', 45, gender='M', job='Engineer')name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}
調用方式3:
>>> extra = {'city': 'Beijing', 'job': 'Engineer'}>>> person('Jack', 24, **extra)name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}
命名關鍵字參數
定義方式:
def person(name, age, *args, city, job):print(name, age, args, city, job)
或
def person(name, age, *, city='Beijing', job):print(name, age, city, job)
調用方式:
>>> person('Jack', 24, job='Engineer')Jack 24 Beijing Engineer 要特別注意,如果沒有可變參數,就必須加一個作為特殊分隔字元。如果缺少,Python解譯器將無法識別位置參數和命名關鍵字參數
小結 Python的函數具有非常靈活的參數形態,既可以實現簡單的調用,又可以傳入非常複雜的參數。 預設參數一定要用不可變對象,如果是可變對象,程式運行時會有邏輯錯誤。 要注意定義可變參數和關鍵字參數的文法: *args是可變參數,args接收的是一個tuple; **kw是關鍵字參數,kw接收的是一個dict。 以及調用函數時如何傳入可變參數和關鍵字參數的文法: 可變參數既可以直接傳入:func(1, 2, 3),又可以先組裝list或tuple,再通過*args傳入:func(*(1, 2, 3)); 關鍵字參數既可以直接傳入:func(a=1, b=2),又可以先組裝dict,再通過**kw傳入:func(**{'a': 1, 'b': 2})。 使用*args和**kw是Python的習慣寫法,當然也可以用其他參數名,但最好使用習慣用法。 命名的關鍵字參數是為了限制調用者可以傳入的參數名,同時可以提供預設值。 定義命名的關鍵字參數在沒有可變參數的情況下不要忘了寫分隔字元*,否則定義的將是位置參數。 進階特性 切片
取一個list或tuple的部分元素是非常常見的操作
取list前三個:
>>> L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack']>>> L[0:3]['Michael', 'Sarah', 'Tracy']
如果第一個索引是0,還可以省略,如L[:3]。
支援倒數切片
>>> L = list(range(100))>>> L[0, 1, 2, 3, ..., 99]
前10個數,每兩個取一個:
>>> L[:10:2][0, 2, 4, 6, 8]
所有數,每5個取一個:
>>> L[::5][0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95]
甚至什麼都不寫,唯寫[:]就可以原樣複製一個list:
>>> L[:][0, 1, 2, 3, ..., 99]
** tuple和字串也可以類似地操作 ** 迭代
在Python中,迭代是通過for ... in來完成
只要是可迭代對象,無論有無下標,都可以迭代,比如dict就可以迭代:
>>> d = {'a': 1, 'b': 2, 'c': 3}>>> for key in d:... print(key)...acb
dict的儲存不是按照list的方式順序排列,所以,迭代出的結果順序很可能不一樣。
預設情況下,dict迭代的是key。如果要迭代value,可以用for value in d.values(),如果要同時迭代key和value,可以用for k, v in d.items()。
如何判斷一個對象是可迭代對象呢。
通過collections模組的Iterable類型判斷:
>>> from collections import Iterable>>> isinstance('abc', Iterable) # str是否可迭代True>>> isinstance([1,2,3], Iterable) # list是否可迭代True>>> isinstance(123, Iterable) # 整數是否可迭代False
如果要對list實作類別似Java那樣的下標迴圈怎麼辦。Python內建的enumerate函數可以把一個list變成索引-元素對,這樣就可以在for迴圈中同時迭代索引和元素本身:
>>> for i, value in enumerate(['A', 'B', 'C']):... print(i, value)...0 A1 B2 C
列表產生式
列表產生式即List Comprehensions,是Python內建的非常簡單卻強大的可以用來建立list的產生式。
舉個例子,要產生list [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]可以用list(range(1, 11)):
>>> list(range(1, 11))[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
如果要產生[1x1, 2x2, 3x3, ..., 10x10],可以這樣:
>>> [x * x for x in range(1, 11)][1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
產生器
建立一個包含100萬個元素的列表,不僅佔用很大的儲存空間,如果我們僅僅需要訪問前面幾個元素,那後面絕大多數元素佔用的空間都白白浪費了。 一邊迴圈一邊計算的機制,稱為產生器:generator。 generator儲存的是演算法,每次調用next(g),就計算出g的下一個元素的值,直到計算到最後一個元素,沒有更多的元素時,拋出StopIteration的錯誤。 建立方法 第一種方法很簡單,只要把一個列表產生式的[]改成(),就建立了一個generator:
>>> L = [x * x for x in range(10)]>>> L[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]>>> g = (x * x for x in range(10))>>> g<generator object <genexpr> at 0x1022ef630>
定義generator的另一種方法。如果一個函數定義中包含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和函數的區別
http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014317799226173f45ce40636141b6abc8424e12b5fb27000最難理解的就是generator和函數的執行流程不一樣。函數是順序執行,遇到return語句或者最後一行函數語句就返回。而變成generator的函數,在每次調用next()的時候執行,遇到yield語句返回,再次執行時從上次返回的yield語句處繼續執行。
最難理解的就是generator和函數的執行流程不一樣。函數是順序執行,遇到return語句或者最後一行函數語句就返回。而變成generator的函數,在每次調用next()的時候執行,遇到yield語句返回,再次執行時從上次返回的yield語句處繼續執行。
迭代器
可以直接作用於for迴圈的資料類型有以下幾種: 一類是集合資料類型,如list、tuple、dict、set、str等; 一類是generator,包括產生器和帶yield的generator function。
這些可以直接作用於for迴圈的對象統稱為可迭代對象:Iterable。
可以使用isinstance()判斷一個對象是否是Iterable對象:
>>> from collections import Iterator>>> isinstance((x for x in range(10)), Iterator)True>>> isinstance([], Iterator)False>>> isinstance({}, Iterator)False>>> isinstance('abc', Iterator)False
產生器都是Iterator對象,但list、dict、str雖然是Iterable,卻不是Iterator。
把list、dict、str等Iterable變成Iterator可以使用iter()函數:
>>> isinstance(iter([]), Iterator)True>>> isinstance(iter('abc'), Iterator)True
你可能會問,為什麼list、dict、str等資料類型不是Iterator。
這是因為Python的Iterator對象表示的是一個資料流,Iterator對象可以被next()函數調用並不斷返回下一個資料,直到沒有資料時拋出StopIteration錯誤。可以把這個資料流看做是一個有序序列,但我們卻不能提前知道序列的長度,只能不斷通過next()函數實現按需計算下一個資料,所以Iterator的計算是惰性的,只有在需要返回下一個資料時它才會計算。
Iterator甚至可以表示一個無限大的資料流,例如全體自然數。而使用list是永遠不可能儲存全體自然數的。 小結 凡是可作用於for迴圈的對象都是Iterable類型; 凡是可作用於next()函數的對象都是Iterator類型,它們表示一個惰性計算的序列; 集合資料類型如list、dict、str等是Iterable但不是Iterator,不過可以通過iter()函數獲得一個Iterator對象。 Python的for迴圈本質上就是通過不斷調用next()函數實現的 函數式編程 高階函數
變數可以指向函數
>>> f = abs>>> f(-10)10
函數名也是變數
>>> abs = 10>>> abs(-10)Traceback (most recent call last): File "<stdin>", line 1, in <module>TypeError: 'int' object is not callable
傳入函數
** 既然變數可以指向函數,函數的參數能接收變數,那麼一個函數就可以接收另一個函數作為參數,這種函數就稱之為高階函數。 **
def add(x, y, f):return f(x) + f(y)
調用
>>> add(-5, 6, abs)11
返回函數 函數作為傳回值
高階函數除了可以接受函數作為參數外,還可以把函數作為結果值返回。
def lazy_sum(*args): def sum(): ax = 0 for n in args: ax = ax + n return ax return sum
當我們調用lazy_sum()時,返回的並不是求和結果,而是求和函數:
>>> f = lazy_sum(1, 3, 5, 7, 9)>>> f<function lazy_sum.<locals>.sum at 0x101c6ed90>
調用函數f時,才真正計算求和的結果:
>>> f()25
在這個例子中,我們在函數lazy_sum中又定義了函數sum,並且,內建函式sum可以引用外部函數lazy_sum的參數和局部變數,當lazy_sum返回函數sum時,相關參數和變數都儲存在返回的函數中,這種稱為“閉包(Closure)”的程式結構擁有極大的威力。
** 請再注意一點,當我們調用lazy_sum()時,每次調用都會返回一個新的函數,即使傳入相同的參數:**
>>> f1 = lazy_sum(1, 3, 5, 7, 9)>>> f2 = lazy_sum(1, 3, 5, 7, 9)>>> f1==f2False
閉包
注意到返回的函數在其定義內部引用了局部變數args,所以,當一個函數返回了一個函數後,其內部的局部變數還被新函數引用,所以,閉包用起來簡單,實現起來可不容易。
def count(): fs = [] for i in range(1, 4): def f(): return i*i fs.append(f) return fsf1, f2, f3 = count()>>> f1()9>>> f2()9>>> f3()9
全部都是9。原因就在於返回的函數引用了變數i,但它並非立刻執行。等到3個函數都返回時,它們所引用的變數i已經變成了3,因此最終結果為9。
返回閉包時牢記的一點就是:返回函數不要引用任何迴圈變數,或者後續會發生變化的變數。
如果一定要引用迴圈變數怎麼辦。方法是再建立一個函數,用該函數的參數綁定迴圈變數當前的值,無論該迴圈變數後續如何更改,已綁定到函數參數的值不變:
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)立刻被執行,因此i的當前值被傳入f() return fs
再看看結果:
>>> f1, f2, f3 = count()>>> f1()1>>> f2()4>>> f3()