python面向對象編程

1、面向對象編程

在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類,就只須要知道,建立實例須要給出namescore,而如何打印,都是在Student類的內部定義的,這些數據和邏輯被「封裝」起來了,調用很容易,但卻不用知道內部實現的細節。

訪問限制

 

2、網絡編程

用Python進行網絡編程,就是在Python程序自己這個進程內,鏈接別的服務器進程的通訊端口進行通訊。

TCP/IP協議

互聯網協議包含了上百種協議標準,可是最重要的兩個協議是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鏈接。建立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)
View Code

解釋:

建立Socket時,AF_INET指定使用IPv4協議,若是要用更先進的IPv6,就指定爲AF_INET6SOCK_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()
View Code

客戶端

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()
View Code

須要打開兩個命令行窗口,一個運行服務器程序,另外一個運行客戶端程序,就能夠看到效果了 。

須要注意的是,客戶端程序運行完畢就退出了,而服務器程序會永遠運行下去,必須按Ctrl+C退出程序。

 

3、Web開發

Python的誕生歷史比Web還要早,因爲Python是一種解釋型的腳本語言,開發效率高,因此很是適合用來作Web開發。

Python有上百種Web開發框架,有不少成熟的模板技術,選擇Python開發Web應用,不但開發效率高,並且運行速度快。

一個Web應用的本質就是:

  1. 瀏覽器發送一個HTTP請求;

  2. 服務器收到請求,生成一個HTML文檔;

  3. 服務器把HTML文檔做爲HTTP響應的Body發送給瀏覽器;

  4. 瀏覽器收到HTTP響應,從HTTP Body取出HTML文檔並顯示。

最簡單的Web應用就是先把HTML用文件保存好,用一個現成的HTTP服務器軟件,接收用戶請求,從文件中讀取HTML,返回。Apache、Nginx、Lighttpd等這些常見的靜態服務器就是幹這件事情的。

作法是底層代碼由專門的服務器軟件實現,咱們用Python專一於生成HTML文檔。由於咱們不但願接觸到TCP鏈接、HTTP原始請求和響應格式,因此,須要一個統一的接口,讓咱們專心用Python編寫Web業務。

這個接口就是WSGI:Web Server Gateway Interface

WSGI

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()函數怎麼調用?若是咱們本身調用,兩個參數environstart_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')]
View Code

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()
View Code

執行server.py,而後打開瀏覽器:http://localhost:8000/  或者 http://localhost:8000/huy

不管多麼複雜的Web應用程序,入口都是一個WSGI處理函數HTTP請求的全部輸入信息均可以經過environ得到,HTTP響應的輸出均可以經過start_response()加上函數返回值做爲Body

複雜的Web應用程序,光靠一個WSGI函數來處理仍是太底層了,咱們須要在WSGI之上再抽象出Web框架,進一步簡化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()
View Code

Flask自帶的Server在端口5000上監聽,輸入首頁地址http://localhost:5000/

再輸入 http://localhost:5000/signin

輸入預設的用戶名admin和口令password,登陸成功。。輸個錯誤密碼試試。。

實際的Web App應該拿到用戶名和口令後,去數據庫查詢再比對,來判斷用戶是否能登陸成功。

除了Flask,常見的Python Web框架還有:

  • Django:全能型Web框架;

  • web.py:一個小巧的Web框架;

  • Bottle:和Flask相似的Web框架;

  • Tornado:Facebook的開源異步Web框架。

固然了,由於開發Python的Web框架也不是什麼難事,咱們後面也會講到開發Web框架的內容。

有了Web框架,咱們在編寫Web應用時,注意力就從WSGI處理函數轉移到URL+對應的處理函數,這樣,編寫Web App就更加簡單了

在編寫URL處理函數時,除了配置URL外,從HTTP請求拿到用戶數據也是很是重要的。Web框架都提供了本身的API來實現這些功能。Flask經過request.form['name']來獲取表單的內容。

使用模板

使用模板,咱們須要預先準備一個HTML文檔,這個HTML文檔不是普通的HTML,而是嵌入了一些變量和指令,而後,根據咱們傳入的數據,替換後,獲得最終的HTML,發送給用戶:

mvc-seq

這就是傳說中的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()
View Code

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

登陸失敗的模板呢?咱們在form.html中加了一點條件判斷,把form.html重用爲登陸失敗的模板。

最後,必定要把模板放到正確的templates目錄下,templatesapp.py在同級目錄下:

mvc-dir

啓動: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 }}的模板。

 

4、異步IO

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異步io

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()
View Code

@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()
View Code

asyncio提供了完善的異步IO支持;

異步操做須要在coroutine中經過yield from完成

多個coroutine能夠封裝成一組Task而後併發執行

async/await

asyncio提供的@asyncio.coroutine能夠把一個generator標記爲coroutine類型,而後在coroutine內部用yield from調用另外一個coroutine實現異步操做。

爲了簡化並更好地標識異步IO,從Python 3.5開始引入了新的語法asyncawait,可讓coroutine的代碼更簡潔易讀

請注意,asyncawait是針對coroutine的新語法,要使用新的語法,只須要作兩步簡單的替換:

  1. @asyncio.coroutine替換爲async
  2. yield from替換爲await
async def hello():
    print("Hello world!")
    r = await asyncio.sleep(1)
    print("Hello again!")
View Code

aiohttp

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()
View Code

運行後輸入:http://localhost:8080/

相關文章
相關標籤/搜索