首頁  >  問答  >  主體

python - flask中current_app._get_current_object()与current_app有什么区别?

在学习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.

看了这个我还是没懂书中的代码为什么要这样= =。。。

伊谢尔伦伊谢尔伦2721 天前975

全部回覆(2)我來回復

  • PHPz

    PHPz2017-04-17 18:01:56

    這個問題的關鍵在倒數的第三句:

    thr = Thread(target=send_async_email, args=[app, msg])

    因為這裡開了一個單獨的線程,也許你會奇怪了,憑什麼開了一個線程就不能使用 current_app 了?对!就是开了一个线程就不能使用 current_app。原因在于 current_app 的實作。

    current_appFlask 是一个代理,如果你看 Flask 原始碼的話會發現其實它外部包裹的是這樣的:
    源碼位址 line: 48-58

    def _find_app():
        top = _app_ctx_stack.top
        if top is None:
            raise RuntimeError(_app_ctx_err_msg)
        return top.app
    
    ...
    
    current_app = LocalProxy(_find_app)

    這個 LocalProxy 就不展开讲了,但是我可以告诉你这个LocalProxy 的作用就是可以根據線程/協程返回對應當前協程/線程的對象,也就是說

    • 線程 A 往 LocalProxy 中塞入 A

    • 線程 B 往 LocalProxy 中塞入 B

    無論在是什麼地方,

    • 線程 A 永遠取到得是 A,線程 B 取到永遠是 B

    這就是在 Flask 中可以在代码中直接使用 requestcurrent_app 這樣的變數的底層原因。

    所以,答案來了,因為這裡開了一個新線程,如果你不穿真實物件過去,那麼你在線程裡面使用 current_app 将获取不到对象,因为他没有 flask 上下文。這又是一個 Flask 的重要概念,太多得說。

    大概可以這樣說,題主應該可以理解吧。

    回覆
    0
  • 伊谢尔伦

    伊谢尔伦2017-04-17 18:01:56

    我當時有看過這個程式碼,原覺得是一樣的,但為什麼不可以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這麼是否是說這是一個實例?可以這麼理解,當然可以更準確。
    因為無論如何,還是去看原始碼,因為是什麼不重要,設計框架的人反過來寫就能換轉了

    無論是實例還是對象,它們的方法等肯定會有所差異,畢竟代碼是人設計的,這個框架也是人寫的,這裡比較與框架本身有關,過多執著於物件還是實例沒有更多的意義,因為我猜到頭來兩者都不是。

    回覆
    0
  • 取消回覆