python基礎(chǔ)篇大合集,進(jìn)程、裝飾器、列表詳解篇!

進(jìn)程以及狀態(tài)
1. 進(jìn)程
2. 進(jìn)程的狀態(tài)
進(jìn)程的創(chuàng)建-multiprocessing
1. 創(chuàng)建進(jìn)程
2. 進(jìn)程pid
3. Process語法結(jié)構(gòu)如下
4. 給子進(jìn)程指定的函數(shù)傳遞參數(shù)
5. 進(jìn)程間不共享全局變量
進(jìn)程和線程對比
功能
定義的不同
區(qū)別
優(yōu)缺點(diǎn)
進(jìn)程以及狀態(tài)
1. 進(jìn)程
程序:例如xxx.py這是程序,是一個靜態(tài)的
進(jìn)程:一個程序運(yùn)行起來后,代碼+用到的資源 稱之為進(jìn)程,它是操作系統(tǒng)分配資源的基本單元。
不僅可以通過線程完成多任務(wù),進(jìn)程也是可以的
2. 進(jìn)程的狀態(tài)
工作中,任務(wù)數(shù)往往大于cpu的核數(shù),即一定有一些任務(wù)正在執(zhí)行,而另外一些任務(wù)在等待cpu進(jìn)行執(zhí)行,因此導(dǎo)致了有了不同的狀態(tài)。

就緒態(tài):運(yùn)行的條件都已經(jīng)滿足,正在等在cpu執(zhí)行
執(zhí)行態(tài):cpu正在執(zhí)行其功能
等待態(tài):等待某些條件滿足,例如一個程序sleep了,此時就處于等待態(tài)
進(jìn)程的創(chuàng)建-multiprocessing
multiprocessing模塊就是跨平臺版本的多進(jìn)程模塊,提供了一個Process類來代表一個進(jìn)程對象,這個對象可以理解為是一個獨(dú)立的進(jìn)程,可以執(zhí)行另外的事情
1. 創(chuàng)建進(jìn)程
import multiprocessing
import time
def test():
while True:
print("--test--")
time.sleep(1)
if __name__ == "__main__":
p = multiprocessing.Process(target=test)
p.start()
while True:
print("--main--")
time.sleep(1)
說明:
創(chuàng)建子進(jìn)程時,只需要傳入一個執(zhí)行函數(shù)和函數(shù)的參數(shù),創(chuàng)建一個Process實(shí)例,用start()方法啟動
2. 進(jìn)程pid
import multiprocessing
import os
def test():
print("子進(jìn)程在運(yùn)行,pid=%d" % (os.getpid()))
print("子進(jìn)程運(yùn)行結(jié)束")
if __name__ == "__main__":
print("父進(jìn)程在運(yùn)行,pid=%d" % (os.getpid()))
p = multiprocessing.Process(target=test)
p.start()
通過os中的getpid()方法能獲取到當(dāng)前運(yùn)行進(jìn)程的id。
3. Process語法結(jié)構(gòu)如下
Process([group [, target [, name [, args [, kwargs]]]]])
target:如果傳遞了函數(shù)的引用,可以認(rèn)為這個子進(jìn)程就執(zhí)行這里的代碼
args:給target指定的函數(shù)傳遞的參數(shù),以元組的方式傳遞
kwargs:給target指定的函數(shù)傳遞命名參數(shù)
name:給進(jìn)程設(shè)定一個名字,可以不設(shè)定
group:指定進(jìn)程組,大多數(shù)情況下用不到
Process創(chuàng)建的實(shí)例對象的常用方法:
start():啟動子進(jìn)程實(shí)例(創(chuàng)建子進(jìn)程)
is_alive():判斷進(jìn)程子進(jìn)程是否還在活著
join([timeout]):是否等待子進(jìn)程執(zhí)行結(jié)束,或等待多少秒
terminate():不管任務(wù)是否完成,立即終止子進(jìn)程
Process創(chuàng)建的實(shí)例對象的常用屬性:
name:當(dāng)前進(jìn)程的別名,默認(rèn)為Process-N,N為從1開始遞增的整數(shù)
pid:當(dāng)前進(jìn)程的pid(進(jìn)程號)
4. 給子進(jìn)程指定的函數(shù)傳遞參數(shù)
import multiprocessing
import os
import time
def test(name, **kwargs):
for i in range(10):
print("子進(jìn)程在運(yùn)行,name=%s, pid=%d" % (name, os.getpid()))
print(kwargs)
time.sleep(0.2)
if __name__ == "__main__":
p = multiprocessing.Process(target=test, args=("zhangsan",), kwargs={"xxoo": 666})
p.start()
time.sleep(1)
p.terminate()
p.join()
運(yùn)行結(jié)果:
子進(jìn)程在運(yùn)行,name=zhangsan, pid=37751
{'xxoo': 666}
子進(jìn)程在運(yùn)行,name=zhangsan, pid=37751
{'xxoo': 666}
子進(jìn)程在運(yùn)行,name=zhangsan, pid=37751
{'xxoo': 666}
子進(jìn)程在運(yùn)行,name=zhangsan, pid=37751
{'xxoo': 666}
子進(jìn)程在運(yùn)行,name=zhangsan, pid=37751
{'xxoo': 666}
5. 進(jìn)程間不共享全局變量
import multiprocessing
import os
import time
g_nums = [11, 33]
def test1():
"""子進(jìn)程要執(zhí)行的代碼"""
print("in test1, pid=%d, g_nums=%s", (os.getpid(), g_nums))
for i in range(4):
g_nums.append(i)
time.sleep(1)
print("in test1, pid=%d, g_nums=%s", (os.getpid(), g_nums))
def test2():
"""子進(jìn)程要執(zhí)行的代碼"""
print("in test2, pid=%d, g_nums=%s", (os.getpid(), g_nums))
if __name__ == "__main__":
p1 = multiprocessing.Process(target=test1)
p1.start()
p1.join()
p2 = multiprocessing.Process(target=test2)
p2.start()
運(yùn)行結(jié)果:
in test1, pid=%d, g_nums=%s (37947, [11, 33])
in test1, pid=%d, g_nums=%s (37947, [11, 33, 0])
in test1, pid=%d, g_nums=%s (37947, [11, 33, 0, 1])
in test1, pid=%d, g_nums=%s (37947, [11, 33, 0, 1, 2])
in test1, pid=%d, g_nums=%s (37947, [11, 33, 0, 1, 2, 3])
in test2, pid=%d, g_nums=%s (37948, [11, 33])
進(jìn)程和線程對比
功能
進(jìn)程,能夠完成多任務(wù),比如 在一臺電腦上能夠同時運(yùn)行多個QQ
線程,能夠完成多任務(wù),比如 一個QQ中的多個聊天窗口
定義的不同
進(jìn)程是系統(tǒng)進(jìn)行資源分配和調(diào)度的一個獨(dú)立單位.
線程是進(jìn)程的一個實(shí)體,是CPU調(diào)度和分派的基本單位,它是比進(jìn)程更小的能獨(dú)立運(yùn)行的基本單位.線程自己基本上不擁有系統(tǒng)資源,只擁有一點(diǎn)在運(yùn)行中必不可少的資源(如程序計數(shù)器,一組寄存器和棧),但是它可與同屬一個進(jìn)程的其他的線程共享進(jìn)程所擁有的全部資源.
區(qū)別
一個程序至少有一個進(jìn)程,一個進(jìn)程至少有一個線程.
線程的劃分尺度小于進(jìn)程(資源比進(jìn)程少),使得多線程程序的并發(fā)性高。
進(jìn)程在執(zhí)行過程中擁有獨(dú)立的內(nèi)存單元,而多個線程共享內(nèi)存,從而極大地提高了程序的運(yùn)行效率
線程不能夠獨(dú)立執(zhí)行,必須依存在進(jìn)程中
可以將進(jìn)程理解為工廠中的一條流水線,而其中的線程就是這個流水線上的工人
優(yōu)缺點(diǎn)
線程和進(jìn)程在使用上各有優(yōu)缺點(diǎn):線程執(zhí)行開銷小,但不利于資源的管理和保護(hù);而進(jìn)程正相反。
開閉原則:
在不修改原函數(shù)及其調(diào)用方式的情況下對原函數(shù)功能進(jìn)行擴(kuò)展
對代碼的修改是封閉
不能修改被裝飾的函數(shù)的源代碼
不能修改被裝飾的函數(shù)的調(diào)用方式
用函數(shù)的方式設(shè)想一下游戲里用槍的場景
1 def game():
2 print('壓子彈')
3 print('槍上膛')
4 print('發(fā)射子彈')
5 game()
6 game()
7 game()
8
9 此時需要給槍增加一個瞄準(zhǔn)鏡,比如狙擊遠(yuǎn)程目標(biāo)時候需要加,狙擊近程目標(biāo)不用加
10 此時上邊的代碼就變成了現(xiàn)在的代碼
11
12 def sight():
13 print('專業(yè)狙擊瞄準(zhǔn)鏡')
14 game()
15 sight()
16 sight()
17 sight()
18 此時的設(shè)計就不符合開閉原則(因?yàn)樾薷牧嗽a及調(diào)用名稱)
裝飾器(python里面的動態(tài)代理)
本質(zhì): 是一個閉包
組成: 函數(shù)+實(shí)參高階函數(shù)+返回值高階函數(shù)+嵌套函數(shù)+語法糖 = 裝飾器
存在的意義: 在不破壞原有函數(shù)和原有函數(shù)調(diào)用的基礎(chǔ)上,給函數(shù)添加新的功能
通用裝飾器寫法:
1 def warpper(fn): # fn是目標(biāo)函數(shù)相當(dāng)于func
2 def inner(*args,**kwargs): # 為目標(biāo)函數(shù)的傳參
3 '''在執(zhí)行目標(biāo)函數(shù)之前操作'''
4 ret = fn(*args,**kwargs) # 調(diào)用目標(biāo)函數(shù),ret是目標(biāo)函數(shù)的返回值
5 '''在執(zhí)行目標(biāo)函數(shù)之后操作'''
6 return ret # 把目標(biāo)函數(shù)返回值返回,保證函數(shù)正常的結(jié)束
7 return inner
8
9 #語法糖
10 @warpper #相當(dāng)于func = warpper(func)
11 def func():
12 pass
13 func() #此時就是執(zhí)行的inner函數(shù)
上邊的場景用裝飾器修改后
1 方式一
2 def game():
3 print('壓子彈')
4 print('槍上膛')
5 print('發(fā)射子彈')
6
7 def sight(fn): # fn接收的是一個函數(shù)
8 def inner():
9 print('安裝專業(yè)狙擊瞄準(zhǔn)鏡')
10 fn() #調(diào)用傳遞進(jìn)來的函數(shù)
11 print('跑路')
12 return inner #返回函數(shù)地址
13
14 game = sight(game) #傳遞game函數(shù)到sight函數(shù)中
15 game()
16
17 執(zhí)行步驟
18 第一步定義兩個函數(shù)game()為普通函數(shù),sight()為裝飾器函數(shù)
19 第二步定義game = sight(game)等于把game函數(shù)當(dāng)做參數(shù)傳遞給sight(fn)裝飾器函數(shù)fn形參
20 第三步執(zhí)行sight(fn),fn在形參位置,相當(dāng)于下邊函數(shù)game()傳參過來等于fn
21 第四步執(zhí)行inner函數(shù),然后return把inner函數(shù)內(nèi)存地址當(dāng)做返回值返回給sight(game)
22 第五步然后執(zhí)行g(shù)ame(),相當(dāng)于執(zhí)行inner函數(shù)
23 第六步,執(zhí)行inner函數(shù),打印'狙擊鏡',執(zhí)行fn()形參,由于fn形參等于game函數(shù),所以執(zhí)行g(shù)ame()函數(shù),打印'壓子彈','上膛','發(fā)射子彈'
24 第七步打印'跑路'
25 第八步把打印的結(jié)果返回給game()
26
27 方式二
28 def sight(fn): # fn接收的是一個函數(shù)
29 def inner():
30 print('安裝專業(yè)狙擊瞄準(zhǔn)鏡')
31 fn() #調(diào)用傳遞進(jìn)來的函數(shù)
32 print('跑路')
33 return inner #返回函數(shù)地址
34
35 @sight #相當(dāng)于game = sight(game)
36 def game():
37 print('壓子彈')
38 print('槍上膛')
39 print('發(fā)射子彈')
40 game()
41
42 執(zhí)行步驟
43 第一步執(zhí)行sight(fn)函數(shù)
44 第二步執(zhí)行@sight,相當(dāng)于把把game函數(shù)與sight裝飾器做關(guān)聯(lián)
45 第三步把game函數(shù)當(dāng)做參數(shù)傳遞給sight(fn)裝飾器函數(shù)fn形參
46 第四步執(zhí)行inner函數(shù),然后return把inner函數(shù)內(nèi)存地址當(dāng)做返回值返回給@sight
47 第五步執(zhí)行g(shù)ame()相當(dāng)相當(dāng)于執(zhí)行inner()函數(shù),因?yàn)锧sight相當(dāng)于game = sight(game)
48 第六步打印'瞄準(zhǔn)鏡
49 第七步執(zhí)行fn函數(shù),因?yàn)閒n等于game函數(shù),所以會執(zhí)行g(shù)ame()函數(shù),打印'壓子彈','上膛','發(fā)射子彈'.fn()函數(shù)執(zhí)行完畢
50 第八步打印'跑路'
51 第九步然后把所有打印的結(jié)果返回給game()
52
53 結(jié)果
54 安裝專業(yè)狙擊瞄準(zhǔn)鏡
55 壓子彈
56 槍上膛
57 發(fā)射子彈
58 跑路
一個簡單的裝飾器實(shí)現(xiàn)
1 def warpper(fn):
2 def inner():
3 print('每次執(zhí)行被裝飾函數(shù)之前都要先經(jīng)過這里')
4 fn()
5 return inner
6 @warpper
7 def func():
8 print('執(zhí)行了func函數(shù)')
9 func()
10
11 結(jié)果
12 每次執(zhí)行被裝飾函數(shù)之前都要先經(jīng)過這里
13 執(zhí)行了func函數(shù)
帶有一個或多個參數(shù)的裝飾器
1 def sight(fn): #fn等于調(diào)用game函數(shù)
2 def inner(*args,**kwargs): #接受到的是元組("bob",123)
3 print('開始游戲')
4 fn(*args,**kwargs) #接受到的所有參數(shù),打散傳遞給user,pwd
5 print('跑路')
6 return inner
7 @sight
8 def game(user,pwd):
9 print('登陸游戲用戶名密碼:',user,pwd)
10 print('壓子彈')
11 print('槍上膛')
12 print('發(fā)射子彈')
13 game('bob','123')
14 結(jié)果
15 開始游戲
16 登陸游戲用戶名密碼: bob 123
17 壓子彈
18 槍上膛
19 發(fā)射子彈
20 跑路
動態(tài)傳遞一個或多個參數(shù)給裝飾器
1 def sight(fn): #調(diào)用game函數(shù)
2 def inner(*args,**kwargs): #接受到的是元組("bob",123)
3 print('開始游戲')
4 fn(*args,**kwargs) #接受到的所有參數(shù),打散傳遞給正常的參數(shù)
5 print('跑路')
6 return inner
7 @sight
8 def game(user,pwd):
9 print('登陸游戲用戶名密碼:',user,pwd)
10 print('壓子彈')
11 print('槍上膛')
12 print('發(fā)射子彈')
13 return '游戲展示完畢'
14 ret = game('bob','123') #傳遞了兩個參數(shù)給裝飾器sight
15 print(ret)
16
17 @sight
18 def car(qq):
19 print('登陸QQ號%s'%qq)
20 print('開始戰(zhàn)車游戲')
21 ret2 = car(110110) #傳遞了一個參數(shù)給裝飾器sight
22 print(ret2)
23 結(jié)果
24 開始游戲
25 登陸游戲用戶名密碼: bob 123
26 壓子彈
27 槍上膛
28 發(fā)射子彈
29 跑路
30 None
31 開始游戲
32 登陸QQ號110110
33 開始戰(zhàn)車游戲
34 跑路
35 None
36 你會發(fā)現(xiàn)這兩個函數(shù)執(zhí)行的返回值都為None,但是我game定義返回值了return '游戲展示完畢',卻沒給返回
裝飾器的返回值
1 為什么我定義了返回值,但是返回值還是None呢,是因?yàn)槲壹词乖趃ame函數(shù)中定義了return '游戲展示完畢'
2 但是裝飾器里只有一個return inner定義返回值,但是這個返回值是返回的inner函數(shù)的內(nèi)存地址的,并不是inner
3 函數(shù)內(nèi)部的return所以默認(rèn)為None,所以應(yīng)該定義一個inner函數(shù)內(nèi)部的return返回值,而且也沒有接收返回值的變量,
4 所以要要設(shè)置ret = fn(*args,**kwargs)和return ret
5
6 def sight(fn): #調(diào)用game函數(shù)
7 def inner(*args,**kwargs): #接受到的是元組("bob",123)
8 print('開始游戲')
9 ret = fn(*args,**kwargs) #接受到的所有參數(shù),打散傳遞給正常的參數(shù)
10 print('跑路')
11 return ret
12 return inner
13 @sight
14 def game(user,pwd):
15 print('登陸游戲用戶名密碼:',user,pwd)
16 print('壓子彈')
17 print('槍上膛')
18 print('發(fā)射子彈')
19 return '游戲展示完畢'
20 ret = game('bob','123') #傳遞了兩個參數(shù)給裝飾器sight
21 print(ret)
22 結(jié)果
23 開始游戲
24 登陸游戲用戶名密碼: bob 123
25 壓子彈
26 槍上膛
27 發(fā)射子彈
28 跑路
29 游戲展示完畢
30
31
32 事例2
33 def wrapper_out(flag): #裝飾器本身的參數(shù)
34 def wrapper(fn): #目標(biāo)函數(shù)
35 def inner(*args,**kwargs): #目標(biāo)函數(shù)需要接受的參數(shù)
36 if flag == True:
37 print('找第三方問問價格行情')
38 ret = fn(*args,**kwargs)
39 print('買到裝備')
40 return ret
41 else:
42 ret = fn(*args,**kwargs)
43 return ret
44 return inner
45 return wrapper
46 #語法糖,@裝飾器
47 @wrapper_out(True)
48 def func(a,b): #被wrapper裝飾
49 print(a,b)
50 print('開黑')
51 return 'func返回值'
52 abc = func('我是參數(shù)1','我是參數(shù)2')
53 print(abc)
54 結(jié)果
55 找第三方問問價格行情
56 我是參數(shù)1 我是參數(shù)2
57 開黑
58 買到裝備
59 func返回值
多個裝飾器同用一個函數(shù)
1 def wrapper1(fn):
2 def inner(*args,**kwargs):
3 print('wrapper1-1')
4 ret = fn(*args,**kwargs)
5 print('wrapper1-2')
6 return ret
7 return inner
8
9 def wrapper2(fn):
10 def inner(*args,**kwargs):
11 print('wrapper2-1')
12 ret = fn(*args,**kwargs)
13 print('wrapper2-2')
14 return ret
15 return inner
16
17 def wrapper3(fn):
18 def inner(*args,**kwargs):
19 print('wrapper3-1')
20 ret = fn(*args,**kwargs)
21 print('wrapper3-2')
22 return ret
23 return inner
24 @wrapper1
25 @wrapper2
26 @wrapper3
27 def func():
28 print('我是測試小白')
29 func()
30 結(jié)果
31 wrapper1-1
32 wrapper2-1
33 wrapper3-1
34 我是測試小白
35 wrapper3-2
36 wrapper2-2
37 wrapper1-2
python列表類型
分類: python
列表類型簡介
列表類型是一個容器,它里面可以存放任意數(shù)量、任意類型的數(shù)據(jù)。
例如下面的幾個列表中,有存儲數(shù)值的、字符串的、內(nèi)嵌列表的。不僅如此,還可以存儲其他任意類型。
>>> L = [1, 2, 3, 4]
>>> L = ["a", "b", "c", "d"]
>>> L = [1, 2, "c", "d"]
>>> L = [[1, 2, 3], "a", "b", [4, "c"]]
python中的列表是一個序列,其內(nèi)元素是按索引順序進(jìn)行存儲的,可以進(jìn)行索引取值、切片等操作。
列表結(jié)構(gòu)
列表是可變對象,可以原處修改列表中的元素而不會讓列表有任何元數(shù)據(jù)的變動。
>>> L = ["a", "b", "c"]
>>> id(L), id(L[0])
(57028736, 55712192)
>>> L[0] = "aa"
>>> id(L), id(L[0])
(57028736, 56954784)
從id的變動上看,修改列表的第一個元素時,列表本身的id沒有改變,但列表的第一個元素的id已經(jīng)改變。
看了下面列表的內(nèi)存圖示就很容易理解了。

上面是L = ["a", "b", "c"]列表的圖示。變量名L存儲了列表的內(nèi)存地址,列表內(nèi)部包含了類型聲明、列表長度等元數(shù)據(jù),還保存了屬于列表的3個元素的內(nèi)存地址。需要注意的是,列表元素不是直接存在列表范圍內(nèi)的,而是以地址的形式保存在列表中。
所以,修改列表中的元素時,新建一個元素"aa"(之所以新建,是因?yàn)樽址遣豢勺冾愋?,列表本身并沒有改變,只是將列表中指向第一個元素的地址改為新數(shù)據(jù)"aa"的地址。
因?yàn)樾薷牧斜頂?shù)據(jù)不會改變列表本身屬性,這種行為稱為"原處修改"。
所以,列表有幾個主要的的特性:
列表中可以存放、嵌套任意類型的數(shù)據(jù)
列表中存放的是元素的引用,也就是各元素的地址,因此是列表可變對象
列表是可變序列。所以各元素是有位置順序的,可以通過索引取值,可以通過切片取子列表
構(gòu)造列表
有兩種常用的構(gòu)造列表方式:
使用中括號[]
使用list()構(gòu)造方法
使用(中)括號構(gòu)建列表時,列表的元素可以跨行書寫,這是python語法中各種括號類型的特性。
例如:
>>> [1,2,3]
[1, 2, 3]
>>> L = [
1,
2,
3
]
>>> list('abcde')
['a', 'b', 'c', 'd', 'e']
>>> list(range(0, 4))
[0, 1, 2, 3]
上面range()用于生成一系列數(shù)值,就像Linux下的seq命令一樣。但是range()不會直接將數(shù)據(jù)生成出來,它返回的是一個可迭代對象,表示可以一個一個地生成這些數(shù)據(jù),所以這里使用list()將range()的數(shù)據(jù)全部生成出來并形成列表。
中括號方式構(gòu)造列表有一個很重要的特性:列表解析,很多地方也稱為"列表推到"。例如:
>>> [x for x in 'abcdef']
['a', 'b', 'c', 'd', 'e', 'f']
list()是直接將所給定的數(shù)據(jù)一次性全部構(gòu)造出來,直接在內(nèi)存中存放整個列表對象。列表推導(dǎo)方式構(gòu)造列表比list()要快,且性能差距還挺大的。
列表基本操作
列表支持+ *符號操作:
>>> L = [1,2,3,4]
>>> L1 = ['a','b','c']
>>> L + L1
[1, 2, 3, 4, 'a', 'b', 'c']
>>> [1,2] + list("34")
[1, 2, '3', '4']
>>> L * 2
[1, 2, 3, 4, 1, 2, 3, 4]
>>> 2 * L
[1, 2, 3, 4, 1, 2, 3, 4]
可以通過+=的方式進(jìn)行二元賦值:
>>> L1 = [1,2,3,4]
>>> L2= [5,6,7,8]
>>> L1 += L2
>>> L1
[1, 2, 3, 4, 5, 6, 7, 8]
L1 += L2的賦值方式對于可變序列來說(比如這里的列表),性能要好于L1 = L1 + L2的方式。前者直接在L1的原始地址內(nèi)進(jìn)行修改,后者新創(chuàng)建一個列表對象并拷貝原始L1列表。但實(shí)際上,性能的差距是微乎其微的,前面說過列表中保存的是元素的引用,所以拷貝也僅僅只是拷貝一些引用,而非實(shí)際數(shù)據(jù)對象。
列表是序列,序列類型的每個元素都是按索引位置進(jìn)行存放的,所以可以通過索引的方式取得列表元素:
>>> L = [1,2,3,4,5]
>>> L[0]
1
>>> L = [
... [1,2,3,4],
... [11,22,33,44],
... [111,222,333,444]
... ]
>>> L[0][2]
3
>>> L[1][2]
33
>>> L[2][2]
333
當(dāng)然,也可以按索引的方式給給定元素賦值,從而修改列表:
>>> L = [1,2,3,4,5]
>>> L[0] = 11
通過賦值方式修改列表元素時,不僅可以單元素賦值修改,還可以多元素切片賦值。
>>> L[1:3] = [22,33,44,55]
>>> L
[11, 22, 33, 44, 55, 4, 5]
上面對列表的切片進(jìn)行賦值時,實(shí)際上是先取得這些元素,刪除它們,并插入新數(shù)據(jù)的過程。所以上面是先刪除[1:3]的元素,再在這個位置處插入新的列表數(shù)據(jù)。
所以,如果將某個切片賦值為空列表,則表示直接刪除這個元素或這段范圍的元素。
>>> L
[11, 22, 33, 44]
>>> L[1:3] = []
>>> L
[11, 44]
但如果是將空列表賦值給單個索引元素,這不是表示刪除元素,而是表示將空列表作為元素嵌套在列表中。
>>> L = [1,2,3,4]
>>> L[0] = []
>>> L
[[], 2, 3, 4]
這兩種列表賦值的區(qū)別,在理解了前文所說的列表結(jié)構(gòu)之后應(yīng)該不難理順。
列表其它操作
列表是一種序列,所以關(guān)于序列的操作,列表都可以用,比如索引、切片、各種序列可用的函數(shù)(比如append()、extend()、remove()、del、copy()、pop()、reverse())等。詳細(xì)內(nèi)容參見:python序列操作
除了這些序列通用操作,列表還有一個專門的列表方法sort,用于給列表排序。
列表排序sort()和sorted()
sort()是列表類型的方法,只適用于列表;sorted()是內(nèi)置函數(shù),支持各種容器類型。它們都可以排序,且用法類似,但sort()是在原地排序的,不會返回排序后的列表,而sorted()是返回新的排序列表。
>>> help(list.sort)
Help on method_descriptor:
sort(...)
L.sort(key=None, reverse=False) -> None -- stable sort *IN PLACE*
>>> help(sorted)
Help on built-in function sorted in module builtins:
sorted(iterable, /, *, key=None, reverse=False)
Return a new list containing all items from the iterable in ascending order.
A custom key function can be supplied to customize the sort order, and the
reverse flag can be set to request the result in descending order.
本文僅簡單介紹排序用法。
例如列表L:
>>> L = ['python', 'shell', 'Perl', 'Go', 'PHP']
使用sort()和sorted()排序L,注意sort()是對L直接原地排序的,不是通過返回值來體現(xiàn)排序結(jié)果的,所以無需賦值給變量。而sorted()則是返回排序后的新結(jié)果,需要賦值給變量才能保存排序結(jié)果。
>>> sorted(L)
['Go', 'PHP', 'Perl', 'python', 'shell']
>>> L
['python', 'shell', 'Perl', 'Go', 'PHP']
>>> L.sort()
>>> L
['Go', 'PHP', 'Perl', 'python', 'shell']
不難發(fā)現(xiàn),sort()和sorted()默認(rèn)都是升序排序的(A)。它們都可以指定參數(shù)reverse=True來表示順序反轉(zhuǎn),也就是默認(rèn)得到降序:
>>> L.sort(reverse=True)
>>> L
['shell', 'python', 'Perl', 'PHP', 'Go']
在python 3.x中,sort()和sorted()不允許對包含不同數(shù)據(jù)類型的列表進(jìn)行排序。也就是說,如果列表中既有數(shù)值,又有字符串,則排序操作報錯。
sort()和sorted()的另一個參數(shù)是key,它默認(rèn)為key=None,該參數(shù)用來指定自定義的排序函數(shù),從而實(shí)現(xiàn)自己需要的排序規(guī)則。
例如,上面的列表不再按照默認(rèn)的字符順序排序,而是想要按照字符串的長度進(jìn)行排序。所以,自定義這個排序函數(shù):
>>> def sortByLen(s):
... return len(s)
然后通過指定key = sortByLen的參數(shù)方式調(diào)用sort()或sorted(),在此期間還可以指定reverse = True:
>>> L = ['shell', 'python', 'Perl', 'PHP', 'Go']
>>> sorted(L,key=sortByLen)
['Go', 'PHP', 'Perl', 'shell', 'python']
>>> L.sort(key=sortByLen,reverse=True)
>>> L
['python', 'shell', 'Perl', 'PHP', 'Go']
再例如,按照列表每個元素的第二個字符來排序。
def f(e):
return e[1]
L = ['shell', 'python', 'Perl', 'PHP', 'Go']
sorted(L, key=f)
L.sort(key=f)
更多的排序方式,參見:sorting HOWTO。比如指定兩個排序依據(jù),一個按字符串長度升序排,長度相同的按第2個字符降序排。用法其實(shí)很簡單,不過稍占篇幅,所以本文不解釋了。
列表迭代和解析
列表是一個序列,可以使用in測試,使用for迭代。
例如:
>>> L = ["a","b","c","d"]
>>> 'c' in L
True
>>> for i in L:
... print(i)
...
a
b
c
d
再說列表解析,它指的是對序列中(如這里的列表)的每一項(xiàng)元素應(yīng)用一個表達(dá)式,并將表達(dá)式計算后的結(jié)果作為新的序列元素(如這里的列表)。
通俗一點(diǎn)的解釋,以列表序列為例,首先取列表各元素,對每次取的元素都做一番操作,并將操作后得到的結(jié)果放進(jìn)一個新的列表中。
因?yàn)榻馕霾僮魇且粋€元素一個元素追加到新列表中的,所以也稱為"列表推導(dǎo)",表示根據(jù)元素推導(dǎo)列表。
最簡單的,將字符串序列中的各字符取出來放進(jìn)列表中:
>>> [ i for i in "abcdef" ]
['a', 'b', 'c', 'd', 'e', 'f']
這里是列表解析,因?yàn)樗饷媸褂玫氖侵欣ㄌ?span style="outline: 0px;max-width: 100%;box-sizing: border-box;-webkit-tap-highlight-color: transparent;font-weight: 700;border-width: 0px;border-style: initial;border-color: initial;overflow-wrap: break-word !important;">[],表示將操作后的元素放進(jìn)新的列表中??梢詫⒅欣ㄌ柼鎿Q成大括號,就變成了集合解析,甚至字典解析。但注意,沒有直接的元組解析,因?yàn)樵M的括號是特殊的,它會被認(rèn)為是表達(dá)式的優(yōu)先級包圍括號,而不是元組構(gòu)造符號。
取出元素對各元素做一番操作:
>>> [ i * 2 for i in "abcdef" ]
['aa', 'bb', 'cc', 'dd', 'ee', 'ff']
>>> L = [1,2,3,4]
>>> [ i * 2 for i in L ]
[2, 4, 6, 8]
>>> [ (i * 2, i * 3) for i in L ]
[(2, 3), (4, 6), (6, 9), (8, 12)]
解析操作和for息息相關(guān),且都能改寫成for循環(huán)。例如,下面兩個語句得到的結(jié)果是一致的:
[ i * 2 for i in "abcdef" ]
L = []
for i in "abcdef":
L.append(i * 2)
但是解析操作的性能比for循環(huán)要更好,正符合越簡單越高效的理念。
學(xué)過其他語言的人,估計已經(jīng)想到了,解析過程中對各元素的表達(dá)式操作類似于回調(diào)函數(shù)。其實(shí)在python中有一個專門的map()函數(shù),它以第一個參數(shù)作為回調(diào)函數(shù),并返回一個可迭代對象。也就是說,也能達(dá)到和解析一樣的結(jié)果。例如:
>>> def f(x):return x * 2
...
>>> list(map(f,[1,2,3,4]))
[2, 4, 6, 8]
map()函數(shù)在后面的文章會詳細(xì)解釋。

*聲明:本文于網(wǎng)絡(luò)整理,版權(quán)歸原作者所有,如來源信息有誤或侵犯權(quán)益,請聯(lián)系我們刪除或授權(quán)事宜。
