在Python中,全部數據類型均可以視爲對象,固然也能夠自定義對象。自定義的對象數據類型就是面向對象中的類(Class)的概念。html
面向對象的設計思想是抽象出Class,根據Class建立Instance。web
面向對象的抽象程度又比函數要高,由於一個Class既包含數據,又包含操做數據的方法。數據庫
定義以下:django
class Student(object): def __init__(self, name, score): self.name = name self.score = score
class
後面緊接着是類名,即Student
,類名一般是大寫開頭的單詞,緊接着是(object)
,表示該類是從哪一個類繼承下來的,繼承的概念咱們後面再講,一般,若是沒有合適的繼承類,就使用object
類,這是全部類最終都會繼承的類。編程
建立實例是經過類名+()實現的:bart=Student()json
能夠自由地給一個實例變量綁定屬性。flask
和靜態語言不一樣,Python容許對實例變量綁定任何數據,也就是說,對於兩個實例變量,雖然它們都是同一個類的不一樣實例,但擁有的變量名稱均可能不一樣。瀏覽器
好比,給實例bart
綁定一個name
屬性:ruby
>>> bart.name = 'Bart Simpson' >>> bart.name 'Bart Simpson'
在建立實例的時候,能夠將屬性綁定,是經過特殊方法 __init__ (先後分別有兩個下劃線)。服務器
注意到__init__
方法的第一個參數永遠是self
,表示建立的實例自己,所以,在__init__
方法內部,就能夠把各類屬性綁定到self
,由於self
就指向建立的實例自己。
有了__init__
方法,在建立實例的時候,就不能傳入空的參數了,必須傳入與__init__
方法匹配的參數,但self
不須要傳,Python解釋器本身會把實例變量傳進去:
>>> bart = Student('Bart Simpson', 59) >>> bart.name 'Bart Simpson' >>> bart.score 59
外部不直接訪問類中的屬性,而是經過類中的方法來間接訪問。
class Student(object): def __init__(self, name, score): self.name = name self.score = score def print_score(self): print('%s: %s' % (self.name, self.score))
要定義一個方法,除了第一個參數是self
外,其餘和普通函數同樣。要調用一個方法,只須要在實例變量上直接調用,除了self
不用傳遞,其餘參數正常傳入。
這樣一來,咱們從外部看Student
類,就只須要知道,建立實例須要給出name
和score
,而如何打印,都是在Student
類的內部定義的,這些數據和邏輯被「封裝」起來了,調用很容易,但卻不用知道內部實現的細節。
用Python進行網絡編程,就是在Python程序自己這個進程內,鏈接別的服務器進程的通訊端口進行通訊。
互聯網協議包含了上百種協議標準,可是最重要的兩個協議是TCP和IP協議,因此,你們把互聯網的協議簡稱TCP/IP協議。
通訊的時候,雙方必須知道對方的標識,比如發郵件必須知道對方的郵件地址。互聯網上每一個計算機的惟一標識就是IP地址,相似123.123.123.123
。若是一臺計算機同時接入到兩個或更多的網絡,好比路由器,它就會有兩個或多個IP地址,因此,IP地址對應的其實是計算機的網絡接口,一般是網卡。
IP協議負責把數據從一臺計算機經過網絡發送到另外一臺計算機。數據被分割成一小塊一小塊,而後經過IP包發送出去。因爲互聯網鏈路複雜,兩臺計算機之間常常有多條線路,所以,路由器就負責決定如何把一個IP包轉發出去。IP包的特色是按塊發送,途徑多個路由,但不保證能到達,也不保證順序到達。
TCP協議則是創建在IP協議之上的。TCP協議負責在兩臺計算機之間創建可靠鏈接,保證數據包按順序到達。TCP協議會經過握手創建鏈接,而後,對每一個IP包編號,確保對方按順序收到,若是包丟掉了,就自動重發。
許多經常使用的更高級的協議都是創建在TCP協議基礎上的,好比用於瀏覽器的HTTP協議、發送郵件的SMTP協議等。
一個TCP報文除了包含要傳輸的數據外,還包含源IP地址和目標IP地址,源端口和目標端口。
端口有什麼做用?在兩臺計算機通訊時,只發IP地址是不夠的,由於同一臺計算機上跑着多個網絡程序。一個TCP報文來了以後,究竟是交給瀏覽器仍是QQ,就須要端口號來區分。每一個網絡程序都向操做系統申請惟一的端口號,這樣,兩個進程在兩臺計算機之間創建網絡鏈接就須要各自的IP地址和各自的端口號。
一個進程也可能同時與多個計算機創建連接,所以它會申請不少端口。
大多數鏈接都是可靠的TCP鏈接。建立TCP鏈接時,主動發起鏈接的叫客戶端,被動響應鏈接的叫服務器。
舉個例子,經過Socket向百度發請求
import socket s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect(('www.baidu.com',80)) # 發送數據: s.send(b'GET / HTTP/1.1\r\nHost: www.baidu.com\r\nConnection: close\r\n\r\n') # 接收數據: buffer = [] while True: # 每次最多接收1k字節: d = s.recv(1024) if d: buffer.append(d) else: break data = b''.join(buffer) # 關閉鏈接: s.close() header, html = data.split(b'\r\n\r\n', 1) print(header.decode('utf-8')) # 把接收的數據寫入文件: with open('baidu.html', 'wb') as f: f.write(html)
解釋:
建立Socket
時,AF_INET
指定使用IPv4協議,若是要用更先進的IPv6,就指定爲AF_INET6
。SOCK_STREAM
指定使用面向流的TCP協議
客戶端要主動發起TCP鏈接,必須知道服務器的IP地址和端口號,參數是一個tuple
TCP鏈接建立的是雙向通道,雙方均可以同時給對方發數據。可是誰先發誰後發,怎麼協調,要根據具體的協議來決定。例如,HTTP協議規定客戶端必須先發請求給服務器,服務器收到後才發數據給客戶端。
發送的文本格式必須符合HTTP標準,若是格式沒問題,接下來就能夠接收新浪服務器返回的數據了。
接收數據時,調用recv(max)
方法,一次最多接收指定的字節數,所以,在一個while循環中反覆接收,直到recv()
返回空數據,表示接收完畢,退出循環。
接收完數據後,調用close()
方法關閉Socket,這樣,一次完整的網絡通訊就結束了。接收到的數據包括HTTP頭和網頁自己,咱們只須要把HTTP頭和網頁分離一下,把HTTP頭打印出來,網頁內容保存到文件。
服務器進程首先要綁定一個端口並監聽來自其餘客戶端的鏈接。若是某個客戶端鏈接過來了,服務器就與該客戶端創建Socket鏈接,隨後的通訊就靠這個Socket鏈接了。
因此,服務器會打開固定端口(好比80)監聽,每來一個客戶端鏈接,就建立該Socket鏈接。因爲服務器會有大量來自客戶端的鏈接,因此,服務器要可以區分一個Socket鏈接是和哪一個客戶端綁定的。一個Socket依賴4項:服務器地址、服務器端口、客戶端地址、客戶端端口來惟一肯定一個Socket。
可是服務器還須要同時響應多個客戶端的請求,因此,每一個鏈接都須要一個新的進程或者新的線程來處理,不然,服務器一次就只能服務一個客戶端了。
咱們來編寫一個簡單的服務器程序,它接收客戶端鏈接,把客戶端發過來的字符串加上Hello
再發回去。
服務端
import socket import threading import time # 鏈接創建後,服務器首先發一條歡迎消息,而後等待客戶端數據,並加上Hello再發送給客戶端。若是客戶端發送了exit字符串,就直接關閉鏈接。 def tcplink(sock, addr): print('Accept new connection from %s:%s...' % addr) sock.send(b'Welcome!') while True: data = sock.recv(1024) time.sleep(1) if not data or data.decode('utf-8') == 'exit': break sock.send(('Hello, %s!' % data.decode('utf-8')).encode('utf-8')) sock.close() print('Connection from %s:%s closed.' % addr) s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 請注意,小於1024的端口號必需要有管理員權限才能綁定 s.bind(('127.0.0.1',9999)) # 指定等待鏈接的最大數量 s.listen(5) print('Waiting for connection...') while True: # 接受一個新鏈接: sock, addr = s.accept() # 建立新線程來處理TCP鏈接: t = threading.Thread(target=tcplink, args=(sock, addr)) t.start()
客戶端
import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 創建鏈接: s.connect(('127.0.0.1', 9999)) # 接收歡迎消息: print(s.recv(1024).decode('utf-8')) for data in [b'Michael', b'Tracy', b'Sarah']: # 發送數據: s.send(data) print(s.recv(1024).decode('utf-8')) s.send(b'exit') s.close()
須要打開兩個命令行窗口,一個運行服務器程序,另外一個運行客戶端程序,就能夠看到效果了 。
須要注意的是,客戶端程序運行完畢就退出了,而服務器程序會永遠運行下去,必須按Ctrl+C退出程序。
Python的誕生歷史比Web還要早,因爲Python是一種解釋型的腳本語言,開發效率高,因此很是適合用來作Web開發。
Python有上百種Web開發框架,有不少成熟的模板技術,選擇Python開發Web應用,不但開發效率高,並且運行速度快。
一個Web應用的本質就是:
瀏覽器發送一個HTTP請求;
服務器收到請求,生成一個HTML文檔;
服務器把HTML文檔做爲HTTP響應的Body發送給瀏覽器;
瀏覽器收到HTTP響應,從HTTP Body取出HTML文檔並顯示。
最簡單的Web應用就是先把HTML用文件保存好,用一個現成的HTTP服務器軟件,接收用戶請求,從文件中讀取HTML,返回。Apache、Nginx、Lighttpd等這些常見的靜態服務器就是幹這件事情的。
作法是底層代碼由專門的服務器軟件實現,咱們用Python專一於生成HTML文檔。由於咱們不但願接觸到TCP鏈接、HTTP原始請求和響應格式,因此,須要一個統一的接口,讓咱們專心用Python編寫Web業務。
這個接口就是WSGI:Web Server Gateway Interface。
WSGI接口定義很是簡單,它只要求Web開發者實現一個函數,就能夠響應HTTP請求。咱們來看一個最簡單的Web版本的「Hello, web!」:
def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) return [b'<h1>Hello, web!</h1>']
上面的application()
函數就是符合WSGI標準的一個HTTP處理函數,它接收兩個參數:
environ:一個包含全部HTTP請求信息的dict
對象;
start_response:一個發送HTTP響應的函數,只能調一次。
函數的返回值b'<h1>Hello, web!</h1>'
將做爲HTTP響應的Body發送給瀏覽器。
有了WSGI,咱們關心的就是如何從environ
這個dict
對象拿到HTTP請求信息,而後構造HTML,經過start_response()
發送Header,最後返回Body。
整個application()
函數自己沒有涉及到任何解析HTTP的部分,也就是說,底層代碼不須要咱們本身編寫,咱們只負責在更高層次上考慮如何響應請求就能夠了。
不過,等等,這個application()
函數怎麼調用?若是咱們本身調用,兩個參數environ
和start_response
咱們無法提供,返回的bytes
也無法發給瀏覽器。
因此application()
函數必須由WSGI服務器來調用。有不少符合WSGI規範的服務器,咱們能夠挑選一個來用。可是如今,咱們只想儘快測試一下咱們編寫的application()
函數真的能夠把HTML輸出到瀏覽器,因此,要趕忙找一個最簡單的WSGI服務器,把咱們的Web應用程序跑起來。
好消息是Python內置了一個WSGI服務器,這個模塊叫wsgiref,它是用純Python編寫的WSGI服務器的參考實現。所謂「參考實現」是指該實現徹底符合WSGI標準,可是不考慮任何運行效率,僅供開發和測試使用。
hello.py
def application(environ,start_response): start_response('200 OK', [('Content-Type', 'text/html')]) #return [b'<h1>Hello, web!</h1>'] #改造一下 body = '<h1>Hello, %s!</h1>' % (environ['PATH_INFO'][1:] or 'web') return [body.encode('utf-8')]
server.py
# 從WSgiref模板導入 from wsgiref.simple_server import make_server # 導入咱們編寫的application函數 from hello import application # 建立一個服務器,IP地址爲空,端口是8000,處理函數是application: httpd = make_server('', 8000, application) print('Serving HTTP on port 8000...') # 開始監聽HTTP請求: httpd.serve_forever()
執行server.py,而後打開瀏覽器:http://localhost:8000/ 或者 http://localhost:8000/huy
不管多麼複雜的Web應用程序,入口都是一個WSGI處理函數。HTTP請求的全部輸入信息均可以經過environ
得到,HTTP響應的輸出均可以經過start_response()
加上函數返回值做爲Body。
複雜的Web應用程序,光靠一個WSGI函數來處理仍是太底層了,咱們須要在WSGI之上再抽象出Web框架,進一步簡化Web開發。
用Python開發一個Web框架十分容易,因此Python有上百個開源的Web框架。這裏咱們先不討論各類Web框架的優缺點,直接選擇一個比較流行的Web框架——Flask來使用。
先用pip
安裝Flask: pip install flask
而後寫一個xxx.py
,處理3個URL,分別是:
GET /
:首頁,返回Home
;
GET /signin
:登陸頁,顯示登陸表單;
POST /signin
:處理登陸表單,顯示登陸結果。
注意噢,同一個URL/signin
分別有GET和POST兩種請求,映射到兩個處理函數中。
Flask經過Python的裝飾器在內部自動地把URL和函數給關聯起來,
from flask import Flask from flask import request app = Flask(__name__) @app.route('/',methods=['GET','POST']) def home(): return '<h1>home page</h1>' @app.route('/signin',methods=['GET']) def signin_form(): return '''<form action="/signin" method="post"> <p><input name="username"></p> <p><input name="password" type="password"></p> <p><button type="submit">Sign In</button></p> </form>''' @app.route('/signin', methods=['POST']) def signin(): # 須要從request對象讀取表單內容: if request.form['username']=='admin' and request.form['password']=='password': return '<h3>Hello, admin!</h3>' return '<h3>Bad username or password.</h3>' if __name__ == '__main__': app.run()
Flask自帶的Server在端口5000
上監聽,輸入首頁地址http://localhost:5000/
:
再輸入 http://localhost:5000/signin
輸入預設的用戶名admin
和口令password
,登陸成功。。輸個錯誤密碼試試。。
實際的Web App應該拿到用戶名和口令後,去數據庫查詢再比對,來判斷用戶是否能登陸成功。
除了Flask,常見的Python Web框架還有:
固然了,由於開發Python的Web框架也不是什麼難事,咱們後面也會講到開發Web框架的內容。
有了Web框架,咱們在編寫Web應用時,注意力就從WSGI處理函數轉移到URL+對應的處理函數,這樣,編寫Web App就更加簡單了。
在編寫URL處理函數時,除了配置URL外,從HTTP請求拿到用戶數據也是很是重要的。Web框架都提供了本身的API來實現這些功能。Flask經過request.form['name']
來獲取表單的內容。
使用模板,咱們須要預先準備一個HTML文檔,這個HTML文檔不是普通的HTML,而是嵌入了一些變量和指令,而後,根據咱們傳入的數據,替換後,獲得最終的HTML,發送給用戶:
這就是傳說中的MVC:Model-View-Controller,中文名「模型-視圖-控制器」。
Python處理URL的函數就是C:Controller,Controller負責業務邏輯,好比檢查用戶名是否存在,取出用戶信息等等;
包含變量{{ name }}
的模板就是V:View,View負責顯示邏輯,經過簡單地替換一些變量,View最終輸出的就是用戶看到的HTML。
MVC中的Model在哪?Model是用來傳給View的,這樣View在替換變量的時候,就能夠從Model中取出相應的數據。
上面的例子中,Model就是一個dict
:{ 'name': 'Michael' }
只是由於Python支持關鍵字參數,不少Web框架容許傳入關鍵字參數,而後,在框架內部組裝出一個dict
做爲Model。
如今,咱們把上次直接輸出字符串做爲HTML的例子用高端大氣上檔次的MVC模式改寫一下:
from flask import Flask, request, render_template app = Flask(__name__) @app.route('/', methods=['GET', 'POST']) def home(): return render_template('home.html') @app.route('/signin', methods=['GET']) def signin_form(): return render_template('form.html') @app.route('/signin', methods=['POST']) def signin(): username = request.form['username'] password = request.form['password'] if username=='admin' and password=='password': return render_template('signin-ok.html', username=username) return render_template('form.html', message='Bad username or password', username=username) if __name__ == '__main__': app.run()
Flask經過render_template()
函數來實現模板的渲染。和Web框架相似,Python的模板也有不少種。Flask默認支持的模板是jinja2,因此咱們先直接安裝jinja2:
$ pip install jinja2
而後,開始編寫jinja2模板:
home.html 用來顯示首頁的模板: <html> <head> <title>Home</title> </head> <body> <h1 style="font-style:italic">Home</h1> </body> </html> form.html 用來顯示登陸表單的模板: <html> <head> <title>Please Sign In</title> </head> <body> {% if message %} <p style="color:red">{{ message }}</p> {% endif %} <form action="/signin" method="post"> <legend>Please sign in:</legend> <p><input name="username" placeholder="Username" value="{{ username }}"></p> <p><input name="password" placeholder="Password" type="password"></p> <p><button type="submit">Sign In</button></p> </form> </body> </html> signin-ok.html 登陸成功的模板: <html> <head> <title>Welcome, {{ username }}</title> </head> <body> <p>Welcome, {{ username }}!</p> </body> </html>
登陸失敗的模板呢?咱們在form.html
中加了一點條件判斷,把form.html
重用爲登陸失敗的模板。
最後,必定要把模板放到正確的templates
目錄下,templates
和app.py
在同級目錄下:
啓動:http://localhost:5000/
再輸入 http://localhost:5000/signin
在Jinja2模板中,咱們用{{ name }}
表示一個須要替換的變量。不少時候,還須要循環、條件判斷等指令語句,在Jinja2中,用{% ... %}
表示指令。
好比循環輸出頁碼:
{% for i in page_list %}
<a href="/page/{{ i }}">{{ i }}</a> {% endfor %}
若是page_list
是一個list:[1, 2, 3, 4, 5]
,上面的模板將輸出5個超連接。
除了Jinja2,常見的模板還有:
Mako:用<% ... %>
和${xxx}
的一個模板;
Cheetah:也是用<% ... %>
和${xxx}
的一個模板;
Django:Django是一站式框架,內置一個用{% ... %}
和{{ xxx }}
的模板。
CPU的速度遠遠快於磁盤、網絡等IO。在一個線程中,CPU執行代碼的速度極快,然而,一旦遇到IO操做,如讀寫文件、發送網絡數據時,就須要等待IO操做完成,才能繼續進行下一步操做。這種狀況稱爲同步IO。
由於一個IO操做就阻塞了當前線程,致使其餘代碼沒法執行,因此咱們必須使用多線程或者多進程來併發執行代碼,爲多個用戶服務。每一個用戶都會分配一個線程,若是遇到IO致使線程被掛起,其餘用戶的線程不受影響。
多線程和多進程的模型雖然解決了併發問題,可是系統不能無上限地增長線程。因爲系統切換線程的開銷也很大,因此,一旦線程數量過多,CPU的時間就花在線程切換上了,真正運行代碼的時間就少了,結果致使性能嚴重降低。
因爲咱們要解決的問題是CPU高速執行能力和IO設備的龜速嚴重不匹配,多線程和多進程只是解決這一問題的一種方法。
另外一種解決IO問題的方法是異步IO。當代碼須要執行一個耗時的IO操做時,它只發出IO指令,並不等待IO結果,而後就去執行其餘代碼了。一段時間後,當IO返回結果時,再通知CPU進行處理。
異步IO模型須要一個消息循環,在消息循環中,主線程不斷地重複「讀取消息-處理消息」這一過程
又稱微線程,纖程。英文名Coroutine。
子程序,或者稱爲函數,在全部語言中都是層級調用,好比A調用B,B在執行過程當中又調用了C,C執行完畢返回,B執行完畢返回,最後是A執行完畢。因此子程序調用是經過棧實現的,一個線程就是執行一個子程序。
子程序調用老是一個入口,一次返回,調用順序是明確的。而協程的調用和子程序不一樣。
協程看上去也是子程序,但執行過程當中,在子程序內部可中斷,而後轉而執行別的子程序,在適當的時候再返回來接着執行(不是函數調用,有點相似CPU的中斷)。
特色
協程的特色在因而一個線程執行,那和多線程比,協程有何優點?
最大的優點就是協程極高的執行效率。由於子程序切換不是線程切換,而是由程序自身控制,所以,沒有線程切換的開銷,和多線程比,線程數量越多,協程的性能優點就越明顯。
第二大優點就是不須要多線程的鎖機制,由於只有一個線程,也不存在同時寫變量衝突,在協程中控制共享資源不加鎖,只須要判斷狀態就行了,因此執行效率比多線程高不少。
由於協程是一個線程執行,那怎麼利用多核CPU呢?最簡單的方法是多進程+協程,既充分利用多核,又充分發揮協程的高效率,可得到極高的性能。
Python對協程的支持是經過generator(生成器)實現的。
注:若是一個函數定義中包含yield關鍵字,那麼這個函數就再也不是一個普通函數,而是一個generator。。遇到語句返回,再次執行時從上次返回的語句處繼續執行。yieldyield
在generator中,咱們不但能夠經過for
循環來迭代,還能夠不斷調用next()
函數獲取由yield
語句返回的下一個值。
可是Python的yield
不但能夠返回一個值,它還能夠接收調用者發出的參數。
asyncio
的編程模型就是一個消息循環。咱們從asyncio
模塊中直接獲取一個EventLoop
的引用,而後把須要執行的協程扔到EventLoop
中執行,就實現了異步IO。
用asyncio
實現Hello world
代碼以下:
import asyncio @asyncio.coroutine def hello(): print("Hello world!") # 異步調用asyncio.sleep(1): r = yield from asyncio.sleep(1) print("Hello again!") # 獲取EventLoop: loop = asyncio.get_event_loop() # 執行coroutine loop.run_until_complete(hello()) loop.close()
@asyncio.coroutine
把一個generator標記爲coroutine類型(協程),而後,咱們就把這個coroutine
扔到EventLoop
中執行。
hello()
會首先打印出Hello world!
,而後,yield from
語法可讓咱們方便地調用另外一個generator
。因爲asyncio.sleep()
也是一個coroutine
,因此線程不會等待asyncio.sleep()
,而是直接中斷並執行下一個消息循環。當asyncio.sleep()
返回時,線程就能夠從yield from
拿到返回值(此處是None
),而後接着執行下一行語句。
把asyncio.sleep(1)
當作是一個耗時1秒的IO操做,在此期間,主線程並未等待,而是去執行EventLoop
中其餘能夠執行的coroutine
了,所以能夠實現併發執行。
用asyncio
的異步網絡鏈接來獲取sina、sohu和163的網站首頁:
import asyncio @asyncio.coroutine def wget(host): print('wget %s...' % host) connect = asyncio.open_connection(host, 80) reader, writer = yield from connect header = 'GET / HTTP/1.0\r\nHost: %s\r\n\r\n' % host writer.write(header.encode('utf-8')) yield from writer.drain() while True: line = yield from reader.readline() if line == b'\r\n': break print('%s header > %s' % (host, line.decode('utf-8').rstrip())) # Ignore the body, close the socket writer.close() loop = asyncio.get_event_loop() tasks = [wget(host) for host in ['www.sina.com.cn', 'www.sohu.com', 'www.163.com']] loop.run_until_complete(asyncio.wait(tasks)) loop.close()
asyncio
提供了完善的異步IO支持;
異步操做須要在coroutine
中經過yield from
完成;
多個coroutine
能夠封裝成一組Task而後併發執行。
用asyncio
提供的@asyncio.coroutine
能夠把一個generator標記爲coroutine類型,而後在coroutine內部用yield from
調用另外一個coroutine實現異步操做。
爲了簡化並更好地標識異步IO,從Python 3.5開始引入了新的語法async
和await
,可讓coroutine的代碼更簡潔易讀。
請注意,async
和await
是針對coroutine的新語法,要使用新的語法,只須要作兩步簡單的替換:
@asyncio.coroutine
替換爲async
;yield from
替換爲await
。async def hello(): print("Hello world!") r = await asyncio.sleep(1) print("Hello again!")
asyncio
能夠實現單線程併發IO操做。若是僅用在客戶端,發揮的威力不大。若是把asyncio
用在服務器端,例如Web服務器,因爲HTTP鏈接就是IO操做,所以能夠用單線程+coroutine
實現多用戶的高併發支持。
asyncio
實現了TCP、UDP、SSL等協議,aiohttp
則是基於asyncio
實現的HTTP框架。
咱們先安裝aiohttp
:
pip install aiohttp
而後編寫一個HTTP服務器,分別處理如下URL:
/
/about
代碼以下:
import asyncio from aiohttp import web routes = web.RouteTableDef() @routes.get('/') async def index(request): await asyncio.sleep(2) return web.json_response({ 'name': 'index' }) @routes.get('/about') async def about(request): await asyncio.sleep(0.5) return web.Response(text="<h1>about us</h1>") def init(): app = web.Application() app.add_routes(routes) web.run_app(app) init()
運行後輸入:http://localhost:8080/