若想技術精進,當然得把基礎知識打得牢牢的。
廖雪峰的官方網站 ?python3教程,該網站提供的教程淺顯易懂,還附帶了講學視頻,非常適合初學者正規(guī)入門。
以下是通過廖雪峰python官方網站學習的個人查漏補缺。
主要內容包括:1.切片 ?2.迭代 ?3.列表生成式 ?4.生成器 ?5.迭代器 ?6.函數式編程 ?7.map/reduce ?8.filter ?9.sorted ?10.返回函數 ?11.匿名函數 ?12.裝飾器 ?13.偏函數
1. 切片
切片:Python的切片非常靈活,一行代碼就可以實現很多行循環(huán)才能完成的操作。 ?
>>>?L = ['Michael','Sarah','Tracy','Bob','Jack'] ? ??
>>> L[0:3]
['Michael', 'Sarah', 'Tracy']
>>> L[:3]
['Michael', 'Sarah', 'Tracy']
L[0:3]表示,從索引0開始取,直到索引3為止,但不包括索引3。即索引0,1,2,正好是3個元素。如果第一個索引是0,還可以省略:L[:3]。
>>> L = list(range(100))
>>> L[-10:] ? ##取后10個
[90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
>>> L[:10:2] ? ##前10個數,每兩個取一個
[0, 2, 4, 6, 8]
>>> L[::5] ? ##所有數,每5個取一個
[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95]
tuple也是一種list,唯一區(qū)別是tuple不可變。因此,tuple也可以用切片操作,只是操作的結果仍是tuple。
字符串'xxx'也可以看成是一種list,每個元素就是一個字符。因此,字符串也可以用切片操作,只是操作結果仍是字符串。
2.迭代
很多語言比如C語言,迭代list是通過下標完成的,比如Java代碼:
for (i=0; i<list.length; i++) {
? ? n=list[i];
}
Python對list列表和tuple元組迭代通過 ?for ... in 來完成的。 ? 注:list這種數據類型雖然有下標,但很多其他數據類型是沒有下標的,但是,只要是可迭代對象,無論有無下標,都可以迭代,比如dict就可以迭代。
如何判斷一個對象是可迭代對象?方法是通過collections模塊的Iterable類型判斷:
>>> from collections import Iterable
>>> isinstance('abc', Iterable) # str是否可迭代
True
>>> isinstance([1,2,3], Iterable) # list是否可迭代
True
>>> isinstance(123, Iterable) # 整數是否可迭代
False

Python對dict字典的迭代,默認情況下迭代的是key: ?for key in dictionary 來完成。 ?如果要迭代value,可以使用 for value in dictinary.values() 來完成。 ?如果要同時迭代key和vaule,可以使用 ?for k, v in dictionary.items() 來完成。
d={'a':1, 'b':2, 'c':3}
for key in d: ?#默認情況下用key迭代
for value in d.values(): ?#使用value來迭代
for k, v in d.items(): ?#使用key和vaule同時迭代
如果要對list實現類似Java那樣的下標循環(huán)怎么辦?Python內置的enumerate函數可以把一個list變成索引-元素對,這樣就可以在for循環(huán)中同時迭代索引和元素本身:

3.列表生成式
列表生成式:列表生成式即List Comprehensions,是Python內置的非常簡單卻強大的可以用來創(chuàng)建list的生成式。
####生成 list:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>>list(range(1,11))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
####生成 list :[1x1, 2x2, 3x3, ..., 10x10]
>>>[x * x for x in range(1,11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
####for循環(huán)后面還可以加上if判斷,可以篩選出僅偶數的平方
>>> [x * x for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100]
####生成全排列
>>> [m + n for m in 'ABC' for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
運用列表生成式,可以寫出非常簡潔的代碼。例如,列出當前目錄下的所有文件和目錄名,可以通過一行代碼實現:
>>> import os # 導入os模塊
>>> [d for d in os.listdir('.')] ? ? # os.listdir可以列出文件和目錄
###列表生成式也可以使用兩個變量來生成list:
>>> d = {'x': 'A', 'y': 'B', 'z': 'C' }
>>> [k + '=' + v for k, v in d.items()]
['y=B', 'x=A', 'z=C']
###把一個list中所有的字符串變成小寫:
>>> L = ['Hello', 'World', 'IBM', 'Apple']
>>> [s.lower() for s in L]
['hello', 'world', 'ibm', 'apple']
運用列表生成式,可以快速生成list,可以通過一個list推導出另一個list,而代碼卻十分簡潔。
4.生成器
為什么是生成器???——通過列表生成式,我們可以直接創(chuàng)建一個列表。但是,受到內存限制,列表容量肯定是有限的。而且,創(chuàng)建一個包含100萬個元素的列表,不僅占用很大的存儲空間,如果我們僅僅需要訪問前面幾個元素,那后面絕大多數元素占用的空間都白白浪費了。
所以,如果列表元素可以按照某種算法推算出來,那我們是否可以在循環(huán)的過程中不斷推算出后續(xù)的元素呢?這樣就不必創(chuàng)建完整的list,從而節(jié)省大量的空間。在Python中,這種一邊循環(huán)一邊計算的機制,稱為生成器:generator。
要創(chuàng)建一個generator,有很多種方法。第一種方法很簡單,只要把一個列表生成式的[ ]改成( ),就創(chuàng)建了一個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>
####如果要一個一個打印出來,可以通過next()函數獲得generator的下一個返回值:
>>> next(g)
0
####生成器generator也是可迭代對象,使用for循環(huán)迭代
>>> g = (x * x for x in range(10))
>>> for n in g:
... ? ? print(n)
...?
我們創(chuàng)建了一個generator后,基本上永遠不會調用next(),而是通過for循環(huán)來迭代它,并且不需要關心StopIteration的錯誤。generator非常強大。如果推算的算法比較復雜,用類似列表生成式的for循環(huán)無法實現的時候,還可以用函數來實現。
斐波拉契數列用列表生成式寫不出來,但是,用函數把它打印出來卻很容易:

fib函數實際上是定義了斐波拉契數列的推算規(guī)則,可以從第一個元素開始,推算出后續(xù)任意的元素,這種邏輯其實非常類似generator。也就是說,上面的函數和generator僅一步之遙。要把fib函數變成generator,只需要把print(b)改為yield b就可以了:

這里,最難理解的就是generator和函數的執(zhí)行流程不一樣。函數是順序執(zhí)行,遇到return語句或者最后一行函數語句就返回。而變成generator的函數,在每次調用next()的時候執(zhí)行,遇到y(tǒng)ield語句返回,再次執(zhí)行時從上次返回的yield語句處繼續(xù)執(zhí)行。
5.迭代器
可以直接作用于for循環(huán)的數據類型有以下幾種:
一類是集合數據類型,如list列表、tuple元組、dict字典、set集合、str字符串等;
一類是generator,包括生成器和帶yield的generator function。
這些可以直接作用于for循環(huán)的對象統(tǒng)稱為可迭代對象:Iterable。
注:生成器都是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循環(huán)的對象都是Iterable類型;凡是可作用于next()函數的對象都是Iterator類型,它們表示一個惰性計算的序列;集合數據類型如list、dict、str等是Iterable但不是Iterator,不過可以通過iter()函數獲得一個Iterator對象;Python的for循環(huán)本質上就是通過不斷調用next()函數實現的。
小結
1)切片:list、tuple、str都可以切片,且切片操作非常靈活、代碼簡單。
2)迭代:使用for...in xxx: 語句, list、tuple、set、dict、str都是可迭代的。凡是可作用于for循環(huán)的對象都是Iterable類型。
3)列表生成式:使用簡單語句生成列表[ ]
4)生成器:最簡單的方法,將列表生成式[ ] 改為( ) 就是一個生成器; ?將函數改為生成器函數,使用yield,每調用一次都會在這暫時停留一下,你不再調用,我就不再走。一般使用for循環(huán)遍歷。
5)迭代器:凡是可作用于next()函數的對象都是Iterator類型,它們表示一個惰性計算的序列。
6.函數式編程
函數式編程就是一種抽象程度很高的編程范式,純粹的函數式編程語言編寫的函數沒有變量,因此,任意一個函數,只要輸入是確定的,輸出就是確定的,這種純函數我們稱之為沒有副作用。而允許使用變量的程序設計語言,由于函數內部的變量狀態(tài)不確定,同樣的輸入,可能得到不同的輸出,因此,這種函數是有副作用的。
函數式編程的一個特點就是,允許把函數本身作為參數傳入另一個函數,還允許返回一個函數!
1)變量可以指向函數
>>>f=abs ?###變量f,指向函數abs
>>>f(-10)###直接調用abs()函數和調用變量f()完全相同
10
2)函數名也是變量
>>> abs = 10
>>> abs(-10) ? ?###abs已經被更改為整數,其函數功能就不能用了
Traceback (most recent call last):
????File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable
3)傳入函數
既然變量可以指向函數,函數的參數能接收變量,那么一個函數就可以接收另一個函數作為參數,這種函數就稱之為高階函數。
def add(x, y, f):
????return f(x) + f(y)
x = -5
y = 6
f = absf(x) + f(y) ==> abs(-5) + abs(6) ==> 11
把函數作為參數傳入,這樣的函數稱為高階函數,函數式編程就是指這種高度抽象的編程范式。
7.map/reduce
Python內置map()和reduce()函數。
map()高階函數接收兩個參數,一個是函數,一個是Iterable,map將傳入的函數依次作用到序列的每個元素(map函數本質,函數一一作用于元素),并把結果作為新的Iterator返回。
>>> def f(x):
... ???? return x * x
...
>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9]) ?#map(函數名,可迭代對象) 函數一一作用于可迭代對象中的元素,最后返回一個迭代器:Iterator惰性序列 ? ?r。
>>> list(r) ? ?###Iterator迭代器(惰性序列),使用list()將整個序列都計算出來,并返回一個list。
[1, 4, 9, 16, 25, 36, 49, 64, 81]
reduce()把一個函數作用在一個序列[x1, x2, x3, ...]上,這個函數必須接收兩個參數,reduce把結果繼續(xù)和序列的下一個元素做累積計算(reduce函數本質,函數把結果再代入充當其中的一個參數),其效果就是:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
###reduce例子
>>> from functools import reduce
>>> def fn(x, y):
... ???? return x * 10 + y
...
>>> reduce(fn, [1, 3, 5, 7, 9])
13579
8.filter
Python內置filter()函數用于過濾序列。
和map()類似,filter()也接收一個函數和一個序列。和map()不同的是,filter()把傳入的函數依次作用于每個元素,然后根據返回值是True還是False決定保留還是丟棄該元素(filter函數本質,一一作用于元素,True/False決定是否保留)。
def is_odd(n):
????return n % 2 == 1
list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
# 結果: [1, 5, 9, 15]
可見用filter()這個高階函數,關鍵在于正確實現一個“篩選”函數,即能夠返回True/False的函數。filter()函數返回的是一個Iterator迭代器,也就是一個惰性序列,所以要強迫filter()完成計算結果,需要用list()函數獲得所有結果并返回list。
生成素數:
def _odd_iter():###定義一個奇數生成器,生成無限序列
? ? n=1
? ? while True:
? ? ? ? n=n+2
? ? ? ? yield n
############################################
def _not_divisible(n):###定義一個篩選函數
? ? return lambda x: x%n>0
############################################
def primes():###定義一個生成器,不斷返回下一個素數
? ? yield 2
? ? it=_odd_iter() #初始序列
? ? while True:
? ? ? ? n=next(it) #返回序列的第一個數
? ? ? ? yield n
? ? ? ? it=filter(_not_divisible(n), it) #構造新序列
############################################
#打印100以內的素數:
for n in primes():
? ? if n<1000:
? ? ? ? print(n)
? ? else:
break
9.sorted
sorted()函數也是一個高階函數,它還可以接收一個key函數來實現自定義的排序,例如按絕對值大小排序:
>>> sorted([36, 5, -12, 9, -21], key=abs)
[5, 9, -12, -21, 36]
############################################
##### 給sorted傳入key函數,即可實現忽略大小寫的排序:
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)
['Zoo', 'Credit', 'bob', 'about']
sorted()也是一個高階函數。用sorted()排序的關鍵在于實現一個映射函數。高階函數的抽象能力是非常強大的,而且,核心代碼可以保持得非常簡潔。
10.返回函數
函數作為返回值:高階函數除了可以接受函數作為參數外,還可以把函數作為結果值返回。
def lazy_sum(*args):
? ? def sum():
? ? ? ? ax=0
? ? ? ? for n in args:
? ? ? ? ? ? ax=ax+n
? ????? return ax
? ? return sum ?##lazy_sum()函數的返回值為一個函數sum()
內部函數sum()可以引用外部函數lazy_sum的參數和局部變量,當lazy_sum返回函數sum時,相關參數和變量都保存在返回的函數中,這種稱為“閉包(Closure)”的程序結構擁有極大的威力。再注意一點,當我們調用lazy_sum()時,每次調用都會返回一個新的函數,即使傳入相同的參數。
11.匿名函數
當我們在傳入函數時,有些時候,不需要顯式地定義函數,直接傳入匿名函數更方便。
>>> list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
[1, 4, 9, 16, 25, 36, 49, 64, 81]
關鍵字 lambda 表示匿名函數, 冒號前面的 x 表示函數參數。
匿名函數有個限制,就是只能有一個表達式,不用寫return,返回值就是該表達式的結果。用匿名函數有個好處,因為函數沒有名字,不必擔心函數名沖突。此外,匿名函數也是一個函數對象,也可以把匿名函數賦值給一個變量,再利用變量來調用該函數。Python對匿名函數的支持有限,只有一些簡單的情況下可以使用匿名函數。
12.裝飾器
為什么有裝飾器??
1)由于函數也是一個對象,而且函數對象可以被賦值給變量,所以,通過變量也能調用該函數。 ? ?----能懂
eg:abs是python的內置函數, f=abs ? 則,f(-11) ?等同于 abs(-11)
2)函數對象有一個__name__屬性,可以拿到函數的名字。 ? ----能懂
eg:abs函數,通過調用這個函數的name屬性,可以得到其函數名,abs.__name__ ? ? ? ?f也可以 ?f.__name__
3)假設要增強 abs() 函數的功能,比如,在函數調用前后自動打印日志,但又不希望修改 abs() 函數的定義,這種在代碼運行期間動態(tài)增加功能的方式,稱之為“裝飾器”(Decorator)。 ? ? ? ----不是很懂
本質上,decorator就是一個返回函數的高階函數。所以,我們要定義一個能打印日志的decorator,可以定義如下:
####定義一個打印日志的迭代器####
def log(func): ? ?###接收一個函數作為輸入參數
? ? def wrapper(*args, **kw):###定義函數
? ? ? ? print('call %s():' %func.__name__)
? ? ? ? return func(*args, **kw) ?###返回輸入的函數
? ? return wrapper ? ####decorator裝飾器的本質就是返回函數的高階函數,所以此裝飾器要返回函數 ?wrapper
我們要借助Python的@語法,把decorator置于函數的定義處:
@log ?##借助@裝飾器名 ?將其置于函數定義處 ?相當于abs=log(abs)
def abs():
### 由于log()是一個decorator,返回一個函數,所以,原來的abs()函數仍然存在,只是現在同名的abs變量指向了新的函數,于是調用abs()將執(zhí)行新函數,即在log()函數中返回的wrapper()函數。
然后再調用 abs() 函數,不僅會運行 abs() 函數本身,還會在運行abs() 函數前打印一行日志。
一個完整的decorator的寫法:
import functools
def log(func):
? ? @functools.wraps(func)#重要,寫裝飾器的時候加著,防止原函數的__name__屬性值被修改
? ? def wrapper(*args, **kw):
? ? ? ? print('call %s():' %func.__name__)
? ? ? ? return func(*args, **kw)
? ? return wrapper
帶有參數的decorator的寫法:
import functools
def log(text):
? ? def decorator(func):
? ? ? ? @functools.wraps(func)
? ? ? ? def wrapper(*args, **kw):
? ? ? ? ? ? print('%s %s():' %(text, func.__name__))
? ? ? ? ? ? return func(*agrs, **kw)
? ? ? ? return wrapper
? ? return decorator
在面向對象(OOP)的設計模式中,decorator被稱為裝飾模式。OOP的裝飾模式需要通過繼承和組合來實現,而Python除了能支持OOP的decorator外,直接從語法層次支持decorator。Python的decorator可以用函數實現,也可以用類實現。
decorator可以增強函數的功能,定義起來雖然有點復雜,但使用起來非常靈活和方便。
13.偏函數
當函數的參數個數太多,需要簡化時,使用functools.partial可以創(chuàng)建一個新的函數,這個新函數可以固定住原函數的部分參數,從而在調用時更簡單。
int2 = functools.partial(int, base=2) ?#固定參數 ? 轉換成二進制
max2 = functools.partial(max, 10) ?# 實際上會把10作為*args的一部分自動加到左邊
##max2(5,6,7) ?等價于 ?args=(10,5,6,7) ? max(*args)