1. 程式人生 > >python基礎篇大合集,程序、裝飾器、列表詳解篇!

python基礎篇大合集,程序、裝飾器、列表詳解篇!

  • 程序以及狀態
  • 1. 程序
  • 2. 程序的狀態
  • 程序的建立-multiprocessing
  • 1. 建立程序
  • 2. 程序pid
  • 3. Process語法結構如下
  • 4. 給子程序指定的函式傳遞引數
  • 5. 程序間不共享全域性變數
  • 程序和執行緒對比
  • 功能
  • 定義的不同
  • 區別
  • 優缺點

程序以及狀態

python基礎篇大合集,程序、裝飾器、列表詳解篇!

 

1. 程序

程式:例如xxx.py這是程式,是一個靜態的

程序:一個程式執行起來後,程式碼+用到的資源 稱之為程序,它是作業系統分配資源的基本單元。

不僅可以通過執行緒完成多工,程序也是可以的

2. 程序的狀態

工作中,任務數往往大於cpu的核數,即一定有一些任務正在執行,而另外一些任務在等待cpu進行執行,因此導致了有了不同的狀態。

python基礎篇大合集,程序、裝飾器、列表詳解篇!

 

  • 就緒態:執行的條件都已經滿足,正在等在cpu執行
  • 執行態:cpu正在執行其功能
  • 等待態:等待某些條件滿足,例如一個程式sleep了,此時就處於等待態

程序的建立-multiprocessing

multiprocessing模組就是跨平臺版本的多程序模組,提供了一個Process類來代表一個程序物件,這個物件可以理解為是一個獨立的程序,可以執行另外的事情

1. 建立程序

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) 
 

說明:

  • 建立子程序時,只需要傳入一個執行函式和函式的引數,建立一個Process例項,用start()方法啟動

2. 程序pid

import multiprocessing
import os
def test():
 print("子程序在執行,pid=%d" % (os.getpid()))
 print("子程序執行結束")
if __name__ == "__main__":
 print("父程序在執行,pid=%d" % (os.getpid()))
 p = multiprocessing.Process(target=test)
 p.start() 

通過os中的getpid()方法能獲取到當前執行程序的id。

3. Process語法結構如下

Process([group [, target [, name [, args [, kwargs]]]]])

  • target:如果傳遞了函式的引用,可以認為這個子程序就執行這裡的程式碼
  • args:給target指定的函式傳遞的引數,以元組的方式傳遞
  • kwargs:給target指定的函式傳遞命名引數
  • name:給程序設定一個名字,可以不設定
  • group:指定程序組,大多數情況下用不到

Process建立的例項物件的常用方法:

  • start():啟動子程序例項(建立子程序)
  • is_alive():判斷程序子程序是否還在活著
  • join([timeout]):是否等待子程序執行結束,或等待多少秒
  • terminate():不管任務是否完成,立即終止子程序

Process建立的例項物件的常用屬性:

  • name:當前程序的別名,預設為Process-N,N為從1開始遞增的整數
  • pid:當前程序的pid(程序號)

4. 給子程序指定的函式傳遞引數

import multiprocessing
import os
import time
def test(name, **kwargs):
 
 for i in range(10):
 print("子程序在執行,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()

執行結果:

子程序在執行,name=zhangsan, pid=37751
{'xxoo': 666}
子程序在執行,name=zhangsan, pid=37751
{'xxoo': 666}
子程序在執行,name=zhangsan, pid=37751
{'xxoo': 666}
子程序在執行,name=zhangsan, pid=37751
{'xxoo': 666}
子程序在執行,name=zhangsan, pid=37751
{'xxoo': 666}

5. 程序間不共享全域性變數

import multiprocessing
import os
import time
g_nums = [11, 33] 
def test1():
 """子程序要執行的程式碼"""
 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():
 """子程序要執行的程式碼"""
 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()

執行結果:

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])

程序和執行緒對比

功能

  • 程序,能夠完成多工,比如 在一臺電腦上能夠同時執行多個QQ
  • 執行緒,能夠完成多工,比如 一個QQ中的多個聊天視窗

定義的不同

  • 程序是系統進行資源分配和排程的一個獨立單位.
  • 執行緒是程序的一個實體,是CPU排程和分派的基本單位,它是比程序更小的能獨立執行的基本單位.執行緒自己基本上不擁有系統資源,只擁有一點在執行中必不可少的資源(如程式計數器,一組暫存器和棧),但是它可與同屬一個程序的其他的執行緒共享程序所擁有的全部資源.

區別

  • 一個程式至少有一個程序,一個程序至少有一個執行緒.
  • 執行緒的劃分尺度小於程序(資源比程序少),使得多執行緒程式的併發性高。
  • 程序在執行過程中擁有獨立的記憶體單元,而多個執行緒共享記憶體,從而極大地提高了程式的執行效率
  • 執行緒不能夠獨立執行,必須依存在程序中
  • 可以將程序理解為工廠中的一條流水線,而其中的執行緒就是這個流水線上的工人

優缺點

執行緒和程序在使用上各有優缺點:執行緒執行開銷小,但不利於資源的管理和保護;而程序正相反。

開閉原則:

在不修改原函式及其呼叫方式的情況下對原函式功能進行擴充套件

對程式碼的修改是封閉

不能修改被裝飾的函式的原始碼

不能修改被裝飾的函式的呼叫方式

用函式的方式設想一下游戲裡用槍的場景

 1 def game():
 2 print('壓子彈')
 3 print('槍上膛')
 4 print('發射子彈')
 5 game()
 6 game()
 7 game()
 8 
 9 此時需要給槍增加一個瞄準鏡,比如狙擊遠端目標時候需要加,狙擊近程目標不用加
10 此時上邊的程式碼就變成了現在的程式碼
11 
12 def sight():
13 print('專業狙擊瞄準鏡')
14 game()
15 sight()
16 sight()
17 sight()
18 此時的設計就不符合開閉原則(因為修改了原始碼及呼叫名稱)

python基礎篇大合集,程序、裝飾器、列表詳解篇!

 

python基礎篇大合集,程序、裝飾器、列表詳解篇!

 

裝飾器(python裡面的動態代理)

本質: 是一個閉包

組成: 函式+實參高階函式+返回值高階函式+巢狀函式+語法糖 = 裝飾器

存在的意義: 在不破壞原有函式和原有函式呼叫的基礎上,給函式新增新的功能

通用裝飾器寫法:

python基礎篇大合集,程序、裝飾器、列表詳解篇!

 

python基礎篇大合集,程序、裝飾器、列表詳解篇!

 

 1 def warpper(fn): # fn是目標函式相當於func
 2 def inner(*args,**kwargs): # 為目標函式的傳參
 3 '''在執行目標函式之前操作'''
 4 ret = fn(*args,**kwargs) # 呼叫目標函式,ret是目標函式的返回值
 5 '''在執行目標函式之後操作'''
 6 return ret # 把目標函式返回值返回,保證函式正常的結束
 7 return inner
 8 
 9 #語法糖
10 @warpper #相當於func = warpper(func)
11 def func():
12 pass
13 func() #此時就是執行的inner函式

上邊的場景用裝飾器修改後

 1 方式一
 2 def game():
 3 print('壓子彈')
 4 print('槍上膛')
 5 print('發射子彈')
 6 
 7 def sight(fn): # fn接收的是一個函式
 8 def inner():
 9 print('安裝專業狙擊瞄準鏡')
10 fn() #呼叫傳遞進來的函式
11 print('跑路')
12 return inner #返回函式地址
13 
14 game = sight(game) #傳遞game函式到sight函式中
15 game()
16 
17 執行步驟
18 第一步定義兩個函式game()為普通函式,sight()為裝飾器函式
19 第二步定義game = sight(game)等於把game函式當做引數傳遞給sight(fn)裝飾器函式fn形參
20 第三步執行sight(fn),fn在形參位置,相當於下邊函式game()傳參過來等於fn
21 第四步執行inner函式,然後return把inner函式記憶體地址當做返回值返回給sight(game)
22 第五步然後執行game(),相當於執行inner函式
23 第六步,執行inner函式,列印'狙擊鏡',執行fn()形參,由於fn形參等於game函式,所以執行game()函式,列印'壓子彈','上膛','發射子彈'
24 第七步列印'跑路'
25 第八步把列印的結果返回給game()
26 
27 方式二
28 def sight(fn): # fn接收的是一個函式
29 def inner():
30 print('安裝專業狙擊瞄準鏡')
31 fn() #呼叫傳遞進來的函式
32 print('跑路')
33 return inner #返回函式地址
34 
35 @sight #相當於game = sight(game)
36 def game():
37 print('壓子彈')
38 print('槍上膛')
39 print('發射子彈')
40 game()
41 
42 執行步驟
43 第一步執行sight(fn)函式
44 第二步執行@sight,相當於把把game函式與sight裝飾器做關聯
45 第三步把game函式當做引數傳遞給sight(fn)裝飾器函式fn形參
46 第四步執行inner函式,然後return把inner函式記憶體地址當做返回值返回給@sight
47 第五步執行game()相當相當於執行inner()函式,因為@sight相當於game = sight(game)
48 第六步列印'瞄準鏡
49 第七步執行fn函式,因為fn等於game函式,所以會執行game()函式,列印'壓子彈','上膛','發射子彈'.fn()函式執行完畢
50 第八步列印'跑路'
51 第九步然後把所有列印的結果返回給game()
52 
53 結果
54 安裝專業狙擊瞄準鏡
55 壓子彈
56 槍上膛
57 發射子彈
58 跑路

python基礎篇大合集,程序、裝飾器、列表詳解篇!

 

python基礎篇大合集,程序、裝飾器、列表詳解篇!

 

一個簡單的裝飾器實現

 1 def warpper(fn):
 2 def inner():
 3 print('每次執行被裝飾函式之前都要先經過這裡')
 4 fn()
 5 return inner
 6 @warpper
 7 def func():
 8 print('執行了func函式')
 9 func()
10 
11 結果
12 每次執行被裝飾函式之前都要先經過這裡
13 執行了func函式

帶有一個或多個引數的裝飾器

 1 def sight(fn): #fn等於呼叫game函式
 2 def inner(*args,**kwargs): #接受到的是元組("bob",123)
 3 print('開始遊戲')
 4 fn(*args,**kwargs) #接受到的所有引數,打散傳遞給user,pwd
 5 print('跑路')
 6 return inner
 7 @sight
 8 def game(user,pwd):
 9 print('登陸游戲使用者名稱密碼:',user,pwd)
10 print('壓子彈')
11 print('槍上膛')
12 print('發射子彈')
13 game('bob','123')
14 結果
15 開始遊戲
16 登陸游戲使用者名稱密碼: bob 123
17 壓子彈
18 槍上膛
19 發射子彈
20 跑路

動態傳遞一個或多個引數給裝飾器

 1 def sight(fn): #呼叫game函式
 2 def inner(*args,**kwargs): #接受到的是元組("bob",123)
 3 print('開始遊戲')
 4 fn(*args,**kwargs) #接受到的所有引數,打散傳遞給正常的引數
 5 print('跑路')
 6 return inner
 7 @sight
 8 def game(user,pwd):
 9 print('登陸游戲使用者名稱密碼:',user,pwd)
10 print('壓子彈')
11 print('槍上膛')
12 print('發射子彈')
13 return '遊戲展示完畢'
14 ret = game('bob','123') #傳遞了兩個引數給裝飾器sight
15 print(ret)
16 
17 @sight
18 def car(qq):
19 print('登陸QQ號%s'%qq)
20 print('開始戰車遊戲')
21 ret2 = car(110110) #傳遞了一個引數給裝飾器sight
22 print(ret2)
23 結果
24 開始遊戲
25 登陸游戲使用者名稱密碼: bob 123
26 壓子彈
27 槍上膛
28 發射子彈
29 跑路
30 None
31 開始遊戲
32 登陸QQ號110110
33 開始戰車遊戲
34 跑路
35 None
36 你會發現這兩個函式執行的返回值都為None,但是我game定義返回值了return '遊戲展示完畢',卻沒給返回

裝飾器的返回值

 1 為什麼我定義了返回值,但是返回值還是None呢,是因為我即使在game函式中定義了return '遊戲展示完畢'
 2 但是裝飾器裡只有一個return inner定義返回值,但是這個返回值是返回的inner函式的記憶體地址的,並不是inner
 3 函式內部的return所以預設為None,所以應該定義一個inner函式內部的return返回值,而且也沒有接收返回值的變數,
 4 所以要要設定ret = fn(*args,**kwargs)和return ret
 5 
 6 def sight(fn): #呼叫game函式
 7 def inner(*args,**kwargs): #接受到的是元組("bob",123)
 8 print('開始遊戲')
 9 ret = fn(*args,**kwargs) #接受到的所有引數,打散傳遞給正常的引數
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('發射子彈')
19 return '遊戲展示完畢'
20 ret = game('bob','123') #傳遞了兩個引數給裝飾器sight
21 print(ret)
22 結果
23 開始遊戲
24 登陸游戲使用者名稱密碼: bob 123
25 壓子彈
26 槍上膛
27 發射子彈
28 跑路
29 遊戲展示完畢
30 
31 
32 事例2
33 def wrapper_out(flag): #裝飾器本身的引數
34 def wrapper(fn): #目標函式
35 def inner(*args,**kwargs): #目標函式需要接受的引數
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('我是引數1','我是引數2')
53 print(abc)
54 結果
55 找第三方問問價格行情
56 我是引數1 我是引數2
57 開黑
58 買到裝備
59 func返回值

python基礎篇大合集,程序、裝飾器、列表詳解篇!

 

python基礎篇大合集,程序、裝飾器、列表詳解篇!

 

多個裝飾器同用一個函式

python基礎篇大合集,程序、裝飾器、列表詳解篇!

 

python基礎篇大合集,程序、裝飾器、列表詳解篇!

 

 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 結果
31 wrapper1-1
32 wrapper2-1
33 wrapper3-1
34 我是測試小白
35 wrapper3-2
36 wrapper2-2
37 wrapper1-2

python列表型別

分類: python

列表型別簡介

列表型別是一個容器,它裡面可以存放任意數量、任意型別的資料。

例如下面的幾個列表中,有儲存數值的、字串的、內嵌列表的。不僅如此,還可以儲存其他任意型別。

>>> L = [1, 2, 3, 4]
>>> L = ["a", "b", "c", "d"]
>>> L = [1, 2, "c", "d"]
>>> L = [[1, 2, 3], "a", "b", [4, "c"]]

python中的列表是一個序列,其內元素是按索引順序進行儲存的,可以進行索引取值、切片等操作。

列表結構

列表是可變物件,可以原處修改列表中的元素而不會讓列表有任何元資料的變動。

>>> L = ["a", "b", "c"]
>>> id(L), id(L[0])
(57028736, 55712192)
>>> L[0] = "aa"
>>> id(L), id(L[0])
(57028736, 56954784)

從id的變動上看,修改列表的第一個元素時,列表本身的id沒有改變,但列表的第一個元素的id已經改變。

看了下面列表的記憶體圖示就很容易理解了。

python基礎篇大合集,程序、裝飾器、列表詳解篇!

 

上面是L = ["a", "b", "c"]列表的圖示。變數名L儲存了列表的記憶體地址,列表內部包含了型別宣告、列表長度等元資料,還儲存了屬於列表的3個元素的記憶體地址。需要注意的是,列表元素不是直接存在列表範圍內的,而是以地址的形式儲存在列表中

所以,修改列表中的元素時,新建一個元素"aa"(之所以新建,是因為字串是不可變型別),列表本身並沒有改變,只是將列表中指向第一個元素的地址改為新資料"aa"的地址。

因為修改列表資料不會改變列表本身屬性,這種行為稱為"原處修改"。

所以,列表有幾個主要的的特性:

  • 列表中可以存放、巢狀任意型別的資料
  • 列表中存放的是元素的引用,也就是各元素的地址,因此是列表可變物件
  • 列表是可變序列。所以各元素是有位置順序的,可以通過索引取值,可以通過切片取子列表

構造列表

有兩種常用的構造列表方式:

  1. 使用中括號[]
  2. 使用list()構造方法

使用(中)括號構建列表時,列表的元素可以跨行書寫,這是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()用於生成一系列數值,就像Linux下的seq命令一樣。但是range()不會直接將資料生成出來,它返回的是一個可迭代物件,表示可以一個一個地生成這些資料,所以這裡使用list()將range()的資料全部生成出來並形成列表。

中括號方式構造列表有一個很重要的特性:列表解析,很多地方也稱為"列表推到"。例如:

>>> [x for x in 'abcdef']
['a', 'b', 'c', 'd', 'e', 'f']

list()是直接將所給定的資料一次性全部構造出來,直接在記憶體中存放整個列表物件。列表推導方式構造列表比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]

可以通過+=的方式進行二元賦值:

>>> 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的原始地址內進行修改,後者新建立一個列表物件並拷貝原始L1列表。但實際上,效能的差距是微乎其微的,前面說過列表中儲存的是元素的引用,所以拷貝也僅僅只是拷貝一些引用,而非實際資料物件。

列表是序列,序列型別的每個元素都是按索引位置進行存放的,所以可以通過索引的方式取得列表元素:

>>> 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

當然,也可以按索引的方式給給定元素賦值,從而修改列表:

>>> L = [1,2,3,4,5]
>>> L[0] = 11

通過賦值方式修改列表元素時,不僅可以單元素賦值修改,還可以多元素切片賦值。

>>> L[1:3] = [22,33,44,55]
>>> L
[11, 22, 33, 44, 55, 4, 5]

上面對列表的切片進行賦值時,實際上是先取得這些元素,刪除它們,並插入新資料的過程。所以上面是先刪除[1:3]的元素,再在這個位置處插入新的列表資料。

所以,如果將某個切片賦值為空列表,則表示直接刪除這個元素或這段範圍的元素。

>>> L
[11, 22, 33, 44]
>>> L[1:3] = []
>>> L
[11, 44]

但如果是將空列表賦值給單個索引元素,這不是表示刪除元素,而是表示將空列表作為元素巢狀在列表中。

>>> L = [1,2,3,4]
>>> L[0] = []
>>> L
[[], 2, 3, 4]

這兩種列表賦值的區別,在理解了前文所說的列表結構之後應該不難理順。

列表其它操作

列表是一種序列,所以關於序列的操作,列表都可以用,比如索引、切片、各種序列可用的函式(比如append()、extend()、remove()、del、copy()、pop()、reverse())等。詳細內容參見:python序列操作

除了這些序列通用操作,列表還有一個專門的列表方法sort,用於給列表排序。

列表排序sort()和sorted()

sort()是列表型別的方法,只適用於列表;sorted()是內建函式,支援各種容器型別。它們都可以排序,且用法類似,但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直接原地排序的,不是通過返回值來體現排序結果的,所以無需賦值給變數。而sorted()則是返回排序後的新結果,需要賦值給變數才能儲存排序結果。

>>> sorted(L)
['Go', 'PHP', 'Perl', 'python', 'shell']
>>> L
['python', 'shell', 'Perl', 'Go', 'PHP']
>>> L.sort()
>>> L
['Go', 'PHP', 'Perl', 'python', 'shell']

不難發現,sort()和sorted()預設都是升序排序的(A<B<...<Z<a<b<...<z)。它們都可以指定引數reverse=True來表示順序反轉,也就是預設得到降序:

>>> L.sort(reverse=True)
>>> L
['shell', 'python', 'Perl', 'PHP', 'Go']

在python 3.x中,sort()和sorted()不允許對包含不同資料型別的列表進行排序。也就是說,如果列表中既有數值,又有字串,則排序操作報錯。

sort()和sorted()的另一個引數是key,它預設為key=None,該引數用來指定自定義的排序函式,從而實現自己需要的排序規則。

例如,上面的列表不再按照預設的字元順序排序,而是想要按照字串的長度進行排序。所以,自定義這個排序函式:

>>> def sortByLen(s):
... return len(s)

然後通過指定key = sortByLen的引數方式呼叫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。比如指定兩個排序依據,一個按字串長度升序排,長度相同的按第2個字元降序排。用法其實很簡單,不過稍佔篇幅,所以本文不解釋了。

列表迭代和解析

列表是一個序列,可以使用in測試,使用for迭代。

例如:

>>> L = ["a","b","c","d"]
>>> 'c' in L
True
>>> for i in L:
... print(i)
...
a
b
c
d

再說列表解析,它指的是對序列中(如這裡的列表)的每一項元素應用一個表示式,並將表示式計算後的結果作為新的序列元素(如這裡的列表)。

通俗一點的解釋,以列表序列為例,首先取列表各元素,對每次取的元素都做一番操作,並將操作後得到的結果放進一個新的列表中。

因為解析操作是一個元素一個元素追加到新列表中的,所以也稱為"列表推導",表示根據元素推導列表。

最簡單的,將字串序列中的各字元取出來放進列表中:

>>> [ i for i in "abcdef" ]
['a', 'b', 'c', 'd', 'e', 'f']

這裡是列表解析,因為它外面使用的是中括號[],表示將操作後的元素放進新的列表中。可以將中括號替換成大括號,就變成了集合解析,甚至字典解析。但注意,沒有直接的元組解析,因為元組的括號是特殊的,它會被認為是表示式的優先順序包圍括號,而不是元組構造符號。

取出元素對各元素做一番操作:

>>> [ 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息息相關,且都能改寫成for迴圈。例如,下面兩個語句得到的結果是一致的:

[ i * 2 for i in "abcdef" ]
L = []
for i in "abcdef":
 L.append(i * 2)

但是解析操作的效能比for迴圈要更好,正符合越簡單越高效的理念。

學過其他語言的人,估計已經想到了,解析過程中對各元素的表示式操作類似於回撥函式。其實在python中有一個專門的map()函式,它以第一個引數作為回撥函式,並返回一個可迭代物件。也就是說,也能達到和解析一樣的結果。例如:

>>> def f(x):return x * 2
...
>>> list(map(f,[1,2,3,4]))
[2, 4, 6, 8]

map()函式在後面的文章會詳細解釋。

python基礎篇大合集,程序、裝飾器、列表詳解篇!