跟著廖雪峰老師學python (2)

若想技術精進,當然得把基礎知識打得牢牢的。

廖雪峰的官方網站 ?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)中同時迭代索引和元素本身:

for循環(huán)里,同時引用了兩個變量,在Python里是很常見的


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就可以了:

函數fib()和生成器fib()執(zhí)行流程不一樣

這里,最難理解的就是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)

?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 〇、前言 本文共108張圖,流量黨請慎重! 歷時1個半月,我把自己學習Python基礎知識的框架詳細梳理了一遍。 ...
    Raxxie閱讀 19,567評論 17 410
  • 基礎1.r''表示''內部的字符串默認不轉義2.'''...'''表示多行內容3. 布爾值:True、False(...
    neo已經被使用閱讀 1,879評論 0 5
  • 從來不認為鮮花玫瑰鉆戒等物質點綴的告白才是最浪漫和刻骨銘心的,相反,那可能都是套路,因為最浪漫的告白是要能感受到對...
    丸丸的八卦日常閱讀 431評論 0 0
  • 平平淡淡的一天下棋ing
    dq920813閱讀 132評論 0 0
  • 十四、你說的是真的嗎?(下) 我要用什么樣的速度生活 才能與你再次相遇,為什么我已經這樣努力了,可是最后的結局卻依...
    墨燔閱讀 304評論 0 2

友情鏈接更多精彩內容