回覆列表
-
1 # java攻城獅
-
2 # 時間客
python和其他程式語言一樣,高階階段是相對初中階段的晉升階段,實際上到達高階這個階段,意味著你能夠獨立完成一個專案模組甚至能夠獨立規劃、推動獨立的中小型專案,那麼達到這個階段需要掌握哪些技能:
1、Django框架;
2、Flask框架;
3、三大資料庫語言(mysql、Redis、MongoDB),尤其是mysql與python的交;
4、Linux作業系統的命令操作;
5、網路爬蟲技術;
6、Requests,Selenium,Scrapy等模組的使用。
等等。
程式設計開發中,尤其是現在模組化開發,標準化的學習教程並不能代表你是否進入了什麼階段。舉個例子:你很熟悉python的所有知識,但一個基本的小專案都做不了,甚至也解決不了任何問題,透過學習過程進入高階其實是沒有意義的。
所以,在具體工作中,我們評估某個人是否進入高階,或者是否可以在所在(記住環境)的團隊中技術水平能夠具有高於一般成員的能力,其實我們就認定你在這個團隊屬於高階水平。
python的高階程式設計往往是根據所需要學習的知識點來衡量的,但這並不能代表你的實際水平,也不能代表你在團隊中屬於高階程式設計師。
知識不代表動手能力。
Python高階程式設計
生成式(推導式)
用於建立list的生成式
>>> list(range(1,11))
[1,2,3,4,5,6,7,8,9,10]
1
2
3
常見用法
>>> [x * x for x in range(1,11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
1
2
3
寫列表生成式時,把要生成的元素 x * x 放到前面,後面跟 for 迴圈,就可以把 list 創建出來
更多示例
>>> [x * x for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100]
>>> d = {"x": "A", "y": "B", "z": "C" }
>>> [k + "=" + v for k, v in d.items()]
["y=B", "x=A", "z=C"]
1
2
3
4
5
6
7
生成器
在 Python 中,一邊迴圈一邊計算的機制,稱為生成器: generator。這種機制可以節省記憶體,提高效能。
1、透過列表建立。即將列表生成式的[]改成()
>>> 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>
1
2
3
4
5
6
注意:生成器物件,可透過for迴圈進行迭代,也可使用next(g)取下一個值
2、透過函式建立
如果一個函式定義中包含 yield 關鍵字,那麼這個函式就不再是一個普通函式,而是一個 generator物件
def odd():
print("step 1")
yield 1
print("step 2")
yield 3
print("step 3")
yield 5
1
2
3
4
5
6
7
>>> o = odd()
>>> next(o)
step 1
1
>>> next(o)
step 2
3
>>> next(o)
step 3
5
>>> next(o)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
例項,生成器實現斐波那契數列
# 生成斐波那契數列,max指定最大範圍
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
return "done"
func = fib(10)
print(next(func))
print(next(func))
print(next(func))
print(next(func))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
注意:generator在執行過程中,遇到 yield就中斷,下次又繼續執行。須要給迴圈設定一個條件來退出,不然就會產生一個無限數列出來
除了使用next函式,生成器還可以使用for迴圈來呼叫,如下
for i in fib(10):
print(i)
1
2
迭代器
可以直接作用於 for 迴圈的物件統稱為可迭代物件: Iterable。可以使用 isinstance()判斷一個物件是否是 Iterable 物件。但是需要注意,可以被 next()函式呼叫並不斷返回下一個值的物件才被稱為迭代器:Iterator,因此生成器一定是迭代器。
>>> from collections import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
1
2
3
4
5
注意:
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance("abc", Iterator)
False
1
2
3
4
5
6
需要特別注意的幾點:
1、凡是可作用於 for 迴圈的物件都是 Iterable 型別
2、凡是可作用於 next()函式的物件都是 Iterator 型別,它們表示一個惰性計算的序列。只有在需要返回下一個資料時它才會計算
3、集合資料型別如 list、 dict、 str 等是 Iterable 但不是 Iterator,我們可以透過 iter()函式將之轉為一個 Iterator 物件
非同步程式設計
I/O密集型
計算密集型
GIL(Global Interpreter Lock)
併發(concurrent)
一個單核處理器同時處理多個任務
並行(parallel)
多個處理器或者多核的處理器同時處理多個不同的任務
單核多執行緒一定是併發,多核多執行緒不一定是並行。只有當多核多執行緒處理任務時,這兩個執行緒被分配到不同的核上執行時,這兩個任務才是並行的。
併發和並行的區別就是一個人同時吃三個饅頭和三個人同時吃三個饅頭的區別
協程
協程(coroutine)最初在1963年被提出,但並沒有受到重視,現在重新火起來,很大一部分原因是Web服務的發展,對伺服器併發的需求。它是一種比執行緒更加輕量級的存在, 協程不被作業系統核心所管理,而完全由程式設計師自己去控制和排程,帶來的好處就是效能得到了很大的提升,不會像執行緒切換那樣消耗資源
背景
在IO密集型的程式中由於IO操作遠遠慢於CPU的操作,所以往往需要CPU去等待IO操作。同步IO下系統需要切換執行緒,讓作業系統可以在IO過程中執行其他的東西。 這樣雖然程式碼是符合人類的思維習慣但是由於大量的執行緒切換帶來了大量的效能的浪費,尤其是IO密集型的程式。
所以人們發明了非同步IO。就是當資料到達的時候觸發我們的回撥,用來減少執行緒切換帶來效能損失。 但是這樣的壞處也是很大的,主要的壞處就是操作被 “分片” 了,程式碼寫的不是 “一氣呵成” ,而是每次來段資料就要判斷 資料夠不夠處理,夠處理就處理,不夠處理就在等等。這樣的程式碼可讀性很低,不符合人類的習慣。
同步IO
同步IO指發出一個功能呼叫時,在沒有得到結果之前,該呼叫就不返回,一直是阻塞狀態。
非同步IO
非同步IO的概念和同步IO相對。通常來講,當一個非同步過程呼叫發出後,呼叫者不能立刻得到結果。實際處理這個呼叫的部件在完成後,透過狀態、通知和回撥來通知呼叫者。
協程的優勢:一個形象的比喻
假設有1個洗衣房,裡面有10臺洗衣機,有一個洗衣工在負責這10臺洗衣機。那麼洗衣房就相當於1個程序,洗衣工就相當1個執行緒。如果有10個洗衣工,就相當於10個執行緒,1個程序是可以開多執行緒的。這就是多執行緒
然而洗衣機洗衣服是需要等待時間的,如果10個洗衣工,1人負責1臺洗衣機,這樣效率肯定會提高,但是很浪費資源。明明1個人能做的事,卻要10個人來做。只是把衣服放進去,開啟開關,這十個人就沒事做了,等衣服洗好再拿出來就可以了。就算很多人來洗衣服,1個人也足以應付了,開好第一臺洗衣機,在等待的時間就可以去處理第二臺洗衣機、第三臺……直到有衣服洗好
手寫協程,最簡示例。執行後可看到A、B兩個任務交替進行
import time
def methodA():
while True:
print("----A 任務---")
yield
print("----A sleep---")
time.sleep(0.5)
def methodB(c):
while True:
print("----B 任務---")
next(c)
print("----B sleep---")
time.sleep(0.5)
if __name__ == "__main__":
a = methodA()
methodB(a)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
使用gevent協程庫,安裝python -m pip install gevent
import gevent
def f1():
for i in range(3):
print("f1 run ", i)
# 用來模擬一個耗時操作
gevent.sleep(2)
def f2():
for i in range(4):
print("f2 run ", i)
# 用來模擬一個耗時操作
gevent.sleep(2)
g1 = gevent.spawn(f1)
g2 = gevent.spawn(f2)
g1.join()
g2.join()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
協程實現併發爬蟲例項:
from gevent import monkey
monkey.patch_all() # 匯入monkey機制,並自動替換Python中的一些原生程式碼,將其替換為gevent框架重寫過的程式碼
import gevent
import urllib.request
def run_task(url):
print("URL:%s" % url)
response = urllib.request.urlopen(url)
html = response.read()
print(html)
if __name__ == "__main__":
urls = ["https://www.baidu.com", "https://www.python.org/", "http://www.gevent.org/"]
greenlets = [gevent.spawn(run_task, url) for url in urls]
gevent.joinall(greenlets)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
擴充套件:IO模型
來自於類Unix系統的概念
阻塞IO模型
A同學用杯子裝水,開啟水龍頭裝滿水然後離開。這一過程就可以看成是使用了阻塞IO模型,因為如果水龍頭沒有水,他也要等到有水並裝滿杯子才能離開去做別的事情。很顯然,這種IO模型是同步的
非阻塞IO模型
B同學也用杯子裝水,開啟水龍頭後發現沒有水,它離開了,過一會他又拿著杯子來看看……在中間離開的這些時間裡,B同學離開了裝水現場(回到使用者程序空間),可以做他自己的事情。這就是非阻塞IO模型。但是它只有是檢查無資料的時候是非阻塞的,在資料到達的時候依然要等待複製資料到使用者空間(等著水將水杯裝滿),因此它還是同步IO。
IO複用模型
這個時候C同學來裝水,發現有一排水龍頭,舍管阿姨告訴他這些水龍頭都還沒有水,等有水了告訴他。於是等啊等(屬於select函式呼叫),過了一會阿姨告訴他有水了,但不知道是哪個水龍頭有水,於是C同學一個個開啟,往杯子裡裝水(recv)。
如果這時候舍管阿姨告訴C同學哪幾個水龍頭有水了,而不需要一個個開啟檢查,則屬於epoll式(對select的增強版本)呼叫。著名的Nginx伺服器即使用的這種方式
訊號驅動IO模型
D同學讓舍管阿姨等有水的時候通知他(註冊訊號函式),沒多久D同學得知有水了,跑去裝水。這還是屬於同步IO,裝水的時間並沒有省(從核心態到使用者態的資料複製)。
非同步IO模型
E同學讓舍管阿姨將杯子裝滿水後通知他。整個過程E同學都可以做別的事情(沒有recv),這是真正的非同步IO。即呼叫aio_read,讓核心等資料準備好,並且複製到使用者程序空間後執行事先指定好的函式。
一般來講:阻塞IO模型、非阻塞IO模型、IO複用模型(select/poll/epoll)、訊號驅動IO模型都屬於同步IO 概念來自於《UNIX網路程式設計卷1》
lambda表示式與函數語言程式設計
lambda表示式實際上就是一種匿名函式,在GUI程式設計中用處很大。
# 普通函式
def f1(x):
return x * x
# lambda表示式
f2 = lambda x: x * x
f2(5)
1
2
3
4
5
6
7
在動態型別語言中,通常函式也是一種物件,其中存放了一段等待執行的指令,函式名也是變數,故函式也可以賦值給變數,因此函式也可以作為返回值
>>> def add():
... return 2 + 1
...
>>> add
<function add at 0x008C28A0>
>>>
1
2
3
4
5
6
示例
def add(a, b):
return a + b
def process(t, func):
print(func(t[0], t[1]))
# 定義一個座標點的元組
point = (2, 8)
process(point, add)
1
2
3
4
5
6
7
8
9
10
閉包
內部函式對外部函式作用域內變數的引用(非全域性變數),則稱內部函式為閉包
最小示例
# 定義一個函式
def test(number):
# 在函式內部再定義一個函式
def test_in(number_in):
return number + number_in
# 返回閉包
return test_in
res = test(10)
print(res(100))
print(res(200))
print(res(300))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
計數器示例
def counter(start=0):
counts = [start]
def incr():
counts[0] += 1
return counts[0]
return incr
count = counter()
print(count())
print(count())
print(count())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
裝飾器
面向物件程式設計中有開放封閉原則,但它也適用於函數語言程式設計,簡單來說,它規定已經實現的功能程式碼不允許被修改,但可以被擴充套件。這種擴充套件是非侵入式的。Python提供的裝飾器就是解決該問題。
import time
def consume(func):
def inner():
start = time.time()
func()
print(time.time() - start)
return inner
@consume
def a():
time.sleep(0.7)
print("-- a --")
def b():
print("-- a --")
def c():
print("-- a --")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
以上程式碼中,@consume實際上只是Python的語法糖,即使我們不使用這個語法糖,照樣可以使用裝飾器,它等價於以下程式碼
f = consume(a)
f()
a = f
1
2
3
裝飾器的常用用途
引入日誌
函式執行時間統計
執行函式前預備處理
執行函式後清理功能
許可權校驗等場景
快取
C語言擴充套件
在Python中呼叫C基本有兩種方式:
1.使用標準庫的ctypes模組。該方式主要用於在Python中呼叫動態連結庫(.dll、.so),當不需要在C程式碼中反呼叫Python程式碼時,推薦使用,簡單高效。使用這種方式,編寫的動態庫無需為Python特別處理,就和編寫普通的動態庫一樣。
2.使用Python提供的一組特定C API(宣告在Python.h中),用C語言為Python編寫模組。在這種方式中,Python可以和C互相呼叫。與Java JNI 相似,但比之更簡潔高效。
需要注意一點,為Python寫C擴充套件時,直譯器的版本需和編譯出的程式的版本匹配,也就是說,本地直譯器是32位,編譯出的C擴充套件程式也必須是32位,64位亦然。
hello.c
#include <windows.h>
void showTips(){
MessageBox(NULL,"Hello Windows","Tips",MB_OK);
}
1
2
3
4
5
6
test01.py
import platform
from ctypes import *
if platform.system() == "Windows":
lib = cdll.LoadLibrary("hello.dll")
lib.showTips()
1
2
3
4
5
6
安裝MinGW環境進行編譯:gcc hello.c -shared -o hello.dll
獲得hello.dll動態庫
第二種使用Python提供的C API寫擴充套件,詳見我的部落格
呼叫系統API
透過ctypes模組可以直接呼叫到作業系統API
如下示例,呼叫Windows系統中的user32.dll庫中的mouse_event函式,移動當前螢幕滑鼠箭頭的位置
import ctypes
ctypes.windll.user32.mouse_event(0x0001|0x8000, 1000, 500, 0, 0)
1
2
安裝第三方封裝的pywin32庫,使用Python進行window程式設計。
python -m pip install pywin32
1
該庫主要有三個模組win32api、win32gui和win32con
Windows API程式設計示例
import win32gui
from win32con import *
def WndProc(hwnd, msg, wParam, lParam):
if msg == WM_PAINT:
hdc, ps = win32gui.BeginPaint(hwnd)
rect = win32gui.GetClientRect(hwnd)
win32gui.DrawText(hdc, "GUI Python", len("GUI Python"), rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER)
win32gui.EndPaint(hwnd, ps)
if msg == WM_DESTROY:
win32gui.PostQuitMessage(0)
return win32gui.DefWindowProc(hwnd, msg, wParam, lParam)
wc = win32gui.WNDCLASS()
wc.hbrBackground = COLOR_BTNFACE + 1
wc.hCursor = win32gui.LoadCursor(0, IDI_APPLICATION)
wc.lpszClassName = "Python no Windows"
wc.lpfnWndProc = WndProc
reg = win32gui.RegisterClass(wc)
hwnd = win32gui.CreateWindow(reg, "Python", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, 0, None)
win32gui.ShowWindow(hwnd, SW_SHOWNORMAL)
win32gui.UpdateWindow(hwnd)
win32gui.PumpMessages()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
MFC 程式設計示例
import win32ui
from win32con import *
from pywin.mfc import window
class MyWnd(window.Wnd):
def __init__(self):
window.Wnd.__init__(self, win32ui.CreateWnd())
self._obj_.CreateWindowEx(WS_EX_CLIENTEDGE, \
win32ui.RegisterWndClass(0, 0, COLOR_WINDOW + 1), \
"MFC", WS_OVERLAPPEDWINDOW, \
(100, 100, 400, 300), None, 0, None)
def OnClose(self):
self.EndModalLoop(0)
def OnPaint(self):
dc, ps = self.BeginPaint()
dc.DrawText("this is MFC",
self.GetClientRect(),
DT_SINGLELINE | DT_CENTER | DT_VCENTER)
self.EndPaint(ps)
w = MyWnd()
w.ShowWindow()
w.UpdateWindow()
w.RunModalLoop(1)