在学习flask开发,书中一段异步发送邮件的代码是这样写的:
from threading import Thread
from flask import current_app, render_template
from flask.ext.mail import Message
from . import mail
def send_async_email(app, msg):
with app.app_context():
mail.send(msg)
def send_email(to, subject, template, **kwargs):
app = current_app._get_current_object()
msg = Message(app.config['FLASKY_MAIL_SUBJECT_PREFIX'] + ' ' + subject,
sender=app.config['FLASKY_MAIL_SENDER'], recipients=[to])
msg.body = render_template(template + '.txt', **kwargs)
msg.html = render_template(template + '.html', **kwargs)
thr = Thread(target=send_async_email, args=[app, msg])
thr.start()
return thr
在send_mail
函数中,程序使用了current_app._get_current_object()
赋值给app
作为当前程序的实例。此处为什么不直接使用current_app
呢?
flask官方文档中是这样解释这个方法_get_current_object()
的:
Return the current object. This is useful if you want the real object behind the proxy at a time for performance reasons or because you want to pass the object into a different context.
看了这个我还是没懂书中的代码为什么要这样= =。。。
這個問題的關鍵在倒數的第三句:
因為這裡開了一個單獨的線程,也許你會奇怪了,憑什麼開了一個線程就不能使用
current_app
了?对!就是开了一个线程就不能使用current_app
。原因在于current_app
的實作。current_app
在Flask
是一个代理,如果你看Flask
原始碼的話會發現其實它外部包裹的是這樣的:源碼位址 line: 48-58
這個
LocalProxy
就不展开讲了,但是我可以告诉你这个LocalProxy
的作用就是可以根據線程/協程返回對應當前協程/線程的對象,也就是說線程 A 往 LocalProxy 中塞入 A
線程 B 往 LocalProxy 中塞入 B
無論在是什麼地方,
線程 A 永遠取到得是 A,線程 B 取到永遠是 B
這就是在
Flask
中可以在代码中直接使用request
、current_app
這樣的變數的底層原因。所以,答案來了,因為這裡開了一個新線程,如果你不穿真實物件過去,那麼你在線程裡面使用
current_app
将获取不到对象,因为他没有flask 上下文
。這又是一個 Flask 的重要概念,太多得說。大概可以這樣說,題主應該可以理解吧。
我當時有看過這個程式碼,原覺得是一樣的,但為什麼不可以app=current_object呢?
所以再看一看,我的觀點是,文檔裡描述提到了current_app是一個全局的對象(global application object),而current_app._get_current_object是獲取對(get an object)象,類似於class下面的staticmethod和class classmethod對應的是不同的東西(這個類比不準確,但不難get到我的意思,本質上有很類似之處)。
看看這個回答的解釋:
然而這麼說也是不對的,看看對_get_current_object的解釋
Return the current object. This is useful if you want the real object behind the proxy at a time for performance reasons or because you want to pass the object into a different context.
這裡寫的是real object這麼是否是說這是一個實例?可以這麼理解,當然可以更準確。
無論是實例還是對象,它們的方法等肯定會有所差異,畢竟代碼是人設計的,這個框架也是人寫的,這裡比較與框架本身有關,過多執著於物件還是實例沒有更多的意義,因為我猜到頭來兩者都不是。因為無論如何,還是去看原始碼,因為是什麼不重要,設計框架的人反過來寫就能換轉了