這篇文章給大家分享的是有關Python中如何使用Tornado的內容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。
經典的 hello world 示例:
import tornado.web
# 視圖
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello World.")
# 路由
application = tornado.web.Application([
(r"/", MainHandler),
(r"/hello", MainHandler),
])
if __name__ == '__main__':
import tornado.ioloop
application.listen(8000)
tornado.ioloop.IOLoop.instance().start()整個過程其實就是在創建一個socket服務端并監聽8000端口。當請求到來時,根據請求中的url和請求方式(post、get或put等)來指定相應的類中的方法來處理本次請求。在上述示例中 url 在路由系統匹配到時,則服務器會給瀏覽器返回 Hello World ,否則返回 404: Not Found(tornado內部定義的值), 即完成一次http請求和響應。
Tornao中的模板語言和django中類似。模板引擎將模板文件載入內存,然后將數據嵌入其中,最終獲取到一個完整的字符串,再將字符串返回給請求者。
不過還是有區別的。Tornado 的模板支持“控制語句”和“表達語句”,控制語句是使用 {% 和 %} 包起來的。例如 {% if len(items) > 2 %}。表達語句是使用 {{ 和 }} 包起來的,例如 {{ items[0] }} 。
控制語句和對應的 Python 語句的格式基本完全相同。支持 if、for、while 和 try,這些語句邏輯結束的位置需要用 {% end %} 做標記。還通過 extends 和 block 語句實現了模板繼承。這些在 template 模塊的代碼文檔中有著詳細的描述。在使用模板前需要在setting中設置模板路徑:"template_path" : "tpl"
使用模板引擎的簡單示例,后端代碼:
import tornado.web
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.render("index.html", k1='v1', k2='v2') # k1和k2是傳給模板引擎處理的內容
application = tornado.web.Application([
(r"/index", IndexHandler),
])
if __name__ == '__main__':
import tornado.ioloop
application.listen(8000)
tornado.ioloop.IOLoop.instance().start()前端代碼,模板語言的使用:
<body>
<h2>Hello World</h2>
<h4>{{ k1 }}</h4>
{% if k2 == 'v2' %}
<h4>k2 == v2</h4>
{% else %}}
<h3>k2 != v2</h3>
{% end %}
</body>上面的前端代碼,最好是統一保存在某個目錄里,比如新建個tpl目錄來存放。把html文件移過去之后,現在render()方法就找不到這個文件了。當然可以改一下參數,把目錄名加進去。不過推薦的做法是把tpl目錄加到配置里去,對上面的代碼進去修改,加入配置信息:
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.render("index.html", k1='v1', k2='v2')
# 配置就是個key-value的字段
settings = {
'template_path': 'tpl'
}
application = tornado.web.Application([
(r"/index", IndexHandler),
], **settings) # application加載配置信息先準備好如下的頁面,在輸入框里填入要搜索的關鍵字,提交后就跳轉到搜索引擎搜索的結果:
<body> <form method="POST" action="/baidu"> <input type="text" name="wd" /> <input type="submit" value="百度一下" /> </form> </body>
后端的代碼:
import tornado.web
class SearchHandler(tornado.web.RequestHandler):
def get(self):
self.render("baidu.html")
def post(self):
wd = self.get_argument('wd')
print(wd)
self.redirect('https://www.baidu.com/s?wd=%s' % wd)
settings = {
'template_path': 'tpl'
}
application = tornado.web.Application([
(r"/baidu", SearchHandler),
], **settings)
if __name__ == '__main__':
import tornado.ioloop
application.listen(8000)
tornado.ioloop.IOLoop.instance().start()上面的示例,post請求最后是用redirect()返回的,這個是頁面的跳轉。
獲取提交的參數的方法有這些:
class LoginHandler(tornado.web.RequestHandler): def post(self): # 獲取URL中以GET形式傳遞的數據 self.get_query_argument() self.get_query_arguments() # 獲取請求體中以POST形式傳遞的數據 self.get_body_argument() self.get_body_arguments() # 從上面2個里都嘗試獲取 self.get_argument() self.get_arguments()
靜態文件是給用戶直接下載的,所以應該單獨存放,并且在配置里注冊對應的目錄。配置的寫法:
settings = {
'template_path': 'tpl', # 模板
'static_path': 'imgs', # 靜態文件
}現在可以根據配置里的名稱去創建一個新的文件夾 static 用來存放靜態文件。然后放張圖片進去。
這里故意不用static作為靜態文件文件夾的名稱,這里只是注冊文件夾,但是前端引用的時候,無論你的靜態文件放在那里,都是用 static/文件名稱 。
加一個img標簽到html里,然后驗證一下效果。注意src里用的是static,而不是文件夾真正的名稱:
<img src="static/test.jpg" />
這里前端引用的是必須用static,不過這個名字也是可以自定義的:
settings = {
'template_path': 'tpl', # 模板
'static_path': 'imgs', # 靜態文件
'static_url_prefix': '/statics/', # 注意兩邊都要有斜杠/
}self.request.cookies : 獲取cookiesself.set_cookie() : 設置cookieself.request.headers : 獲取請求頭self.set_header() : 設置響應頭,如果出現同一個響應頭,會覆蓋self.add_header() : 設置響應頭,如果出現同一個響應頭,則追加
Tornado 沒有提供 session ,所以要用的話,得另外寫。同樣的,緩存也沒有。
最基本的就是上面那些了,這里再補充一點別的。
這個就是模板引擎里的自定義函數。
UIMethod 自定義的是個函數,UIModule 自定義的是個類。
定義
把自定義的函數和自定義的類單獨寫在文件里:
# ui_methods.py
def test1(self): # 這里的self不能去掉
return "TEST1"
def test2(self):
return "TEST2"
# ui_module.py
from tornado.web import UIModule
from tornado import escape
class Test(UIModule):
def render(self, *args, **kwargs):
return escape.xhtml_escape('<h4>UI Module TEST</h4>')注冊
寫一個完整的服務,這里加上注冊的代碼。先導入上面的文件,然后分別在settings里注冊:
import tornado.web
import ui_methods as mt
import ui_modules as md
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render('ui.html')
settings = {
'template_path': 'tpl', # 模板
'static_path': 'static', # 靜態文件,這里不重要
'static_url_prefix': '/statics/', # 注意兩邊都要有斜杠/
'ui_methods': mt,
'ui_modules': md,
}
application = tornado.web.Application([
(r"/ui", MainHandler),
], **settings)
if __name__ == '__main__':
import tornado.ioloop
application.listen(8000)
tornado.ioloop.IOLoop.instance().start()使用
這里只需要看明白前端調用的方法就可以了
<body>
<h2>UI Method</h2>
<p>{{ test1() }}</p>
<p>{{ test2() }}</p>
<h2>UI Module</h2>
{% module Test() %}
</body>UIModule里的方法
render 方法返回的內容就是調用模板的位置顯示的內容:
class Test(UIModule):
def javascript_files(self):
pass
def embedded_javascript(self):
pass
def css_files(self):
pass
def embedded_css(self):
pass
def render(self, *args, **kwargs):
return escape.xhtml_escape('<h4>UI Module TEST</h4>')javascript的方法會在body的尾部插入script標簽,插入js代碼
css的方法則會在head里插入style標簽,設置css
files就是直接引入文件,進行設置
embedded就是插入返回的字符串作為設置
首先在settings里啟用csrf:
settings = {
"xsrf_cookies": True,
}在 form 中使用
<form action="/new_message" method="post">
{% raw xsrf_form_html() %}
<input type="text" name="message"/>
<input type="submit" value="提交"/>
</form>{{ xsrf_form_html() }} 能夠輸出完整的input標簽,但是直接用回被解析為字符串,帶著標簽的信息作為字符串顯示出來。所以上面用的是 {% raw xsrf_form_html() %} 。
在 Ajax 中使用
Ajax使用時,本質上就是去獲取本地的cookie,攜帶cookie再來發送請求:
function getCookie(name) {
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
return r ? r[1] : undefined;
}
jQuery.postJSON = function(url, args, callback) {
args._xsrf = getCookie("_xsrf");
$.ajax({url: url, data: $.param(args), dataType: "text", type: "POST",
success: function(response) {
callback(eval("(" + response + ")"));
}});
};先準備一個form表單上傳文件的html頁面:
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>上傳文件</title> </head> <body> <form id="my_form" name="form" action="/index" method="POST" enctype="multipart/form-data" > <input name="fff" id="my_file" type="file" /> <input type="submit" value="提交" /> </form> </body> </html>
接收上傳文件:
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render('index.html')
def post(self, *args, **kwargs):
file_metas = self.request.files["fff"]
# print(file_metas)
for meta in file_metas:
file_name = meta['filename']
with open(file_name,'wb') as up:
up.write(meta['body'])
settings = {
'template_path': 'template',
}
application = tornado.web.Application([
(r"/index", MainHandler),
], **settings)
if __name__ == '__main__':
import tornado.ioloop
application.listen(8000)
tornado.ioloop.IOLoop.instance().start()上傳文件還可以用Ajax,另外還有借助iframe標簽實現的偽Ajax的實現,略...
異步非阻塞IO,高并發高性能是tornado的特點,所以這小節很重要。但是具體內容也沒搞明白,只能盡量先記一些。
首先要引入下面的2個模塊:
from tornado import gen
from tornado.concurrent import Future
class AsyncHandler(tornado.web.RequestHandler):
@gen.coroutine
def get(self):
future = Future()
future.add_done_callback(self.doing)
yield future
def doing(self,*args, **kwargs):
self.write('async')
self.finish()當發送GET請求時,由于方法被@gen.coroutine裝飾且yield 一個Future對象,那么Tornado會等待,等待用戶向future對象中放置數據或者發送信號,如果獲取到數據或信號之后,就開始執行doing方法。
這里發送請求后,永遠也不會返回,就是按上面說的Tornado一直在等待。等待調用了 future.set_result(result) 這個方法。之后就會調用回調函數,而set_result方法里傳遞進去的參數,可以通過 future.result() 獲取到。大致就是這么的用法,但是沒有個使用示例有點不好理解。
Future類
Future類位于tornado源碼的concurrent模塊中。下面是Future類里的一部分代碼作為分析之用:
class Future(object):
def done(self):
return self._done
def result(self, timeout=None):
self._clear_tb_log()
if self._result is not None:
return self._result
if self._exc_info is not None:
raise_exc_info(self._exc_info)
self._check_done()
return self._result
def add_done_callback(self, fn):
if self._done:
fn(self)
else:
self._callbacks.append(fn)
def set_result(self, result):
self._result = result
self._set_done()
def _set_done(self):
self._done = True
for cb in self._callbacks:
try:
cb(self)
except Exception:
app_log.exception('exception calling callback %r for %r',
cb, self)
self._callbacks = NoneFuture類重要成員函數:
def done(self) : Future的_result成員是否被設置
def result(self, timeout=None) : 獲取Future對象的結果
def add_done_callback(self, fn) : 添加一個回調函數fn給Future對象。如果這個Future對象已經done,則直接執行fn,否則將fn加入到Future類的一個成員列表中保存。
def_set_done(self) : 一個內部函數,主要是遍歷列表,逐個調用列表中的callback函數,也就是前面 add_done_calback 加如來的。
def set_result(self, result) : 給Future對象設置result,并且調用_set_done。也就是說,當Future對象獲得result后,所有add_done_callback加入的回調函數就會執行。
這里最終就是希望 future 調用 set_result ,然后就是執行回調函數。
這節主要是想以源碼的方式展示分析tornado是怎么實現異步非阻塞的。代碼應該不是超的源碼,只是借鑒了思路,做了很多簡化。
下面是實現異步非阻塞的代碼,主要是 select+socket :
https://www.cnblogs.com/wupeiqi/p/6536518.html
復雜的應用還是用django來開發。
如果要開發一個API的功能,或者其他的簡單的工具、應用,也不用操作數據庫。就可以用tornado或者是其他簡單的框架。就不需要用django了。
可以選擇的簡單的框架還有Flask。
感謝各位的閱讀!關于“Python中如何使用Tornado”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。