理解:Flask 在初始化时传入参数的具体作用到底有哪些?
了解或使用过 flask 框架的话,肯定会见过过如下所示的代码,其具体的作用就是实例化 Flask 的对象。因为 Web 服务器使用 WSGI 协议,所以需要把客户端所有的请求都转发给这个程序实例,即这里的 app 实例。
from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello World!' if __name__ == '__main__': app.run(host='0.0.0.0', port=8000)
- 系统变量的规则
- 名为 test.py 的模块,其位于应用程序的顶级目录中,那么 __name__ 变量的值就为 test。
- 如果 test.py 模块位于 my_package 的 Python 包中,则 __name__ 的值就为 my_package.test。
- 系统变量的例外
- 在 __init__.py 包构造函数模块中,__name__ 的值是包名称,不带 __init__。例如,在 my_package/__init__.py 中,__name__ 的值就是 my_package。
- 在应用程序的主模块(运行 Python 解释器的文件)中,__name__ 的值具有特殊值 __main__。
1. 查找资源
系统变量的作用 - 查找文件系统上的资源
Flask 类只有一个必须指定的参数,用于指定程序的主模块或者包的名称,即这里通过会使用到的 __name__ 这个系统变量。实例中的该系统变量,指的就是本 py 文件的文件名。其可以让 flask.helpers.get_root_path 函数通过传入这个名字确定程序的根目录,以便获得静态文件和模板文件的目录。
# Application Object class flask.Flask(import_name, static_url_path=None, static_folder='static', static_host=None, host_matching=False, subdomain_matching=False, template_folder='templates', instance_path=None, instance_relative_config=False, root_path=None)
实际上,很容易理解。通过传入的 import_name 参数(即这里的__name__参数),将其附加到实例化的 app 上下文中,从而告诉 Flask 如何寻找到这些资源文件,比如模板和静态文档等。
它的工作方式如下: Flask 接受了作为 import_name 传入的参数,并试图通过查找具有该名称的模块对象来使用它来确定应用程序的根路径。知道此路径后,它将添加静态目录和模板目录名称,这就是获取这些文件的位置。
>>> from flask.helpers import get_root_path # the app package >>> get_root_path('app') '/home/miguel/microblog/app' # the flask package >>> get_root_path('flask') '/home/miguel/microblog/venv/lib/python3.8/site-packages/flask' # the config.py module >>> get_root_path('config') '/Users/mgrinberg/Documents/dev/python/microblog' # the app/models.py module >>> get_root_path('app.models') '/Users/mgrinberg/Documents/dev/python/microblog/app' # the app.api package >>> get_root_path('app.api') '/Users/mgrinberg/Documents/dev/python/microblog/app/api'
2. 改善调试
系统变量的作用 - 改善 Flask 扩展中的调试信息
弄清楚这一点非常棘手。我发现使用 import_name 参数的唯一 Flask 扩展就是非常有名的 Flask-SQLAlchemy。该扩展程序提供了一个 get_debug_queries() 函数,该函数收集并记录在请求生存期内发出的所有查询。
记录的属性之一是应用程序源代码中发出查询的位置。获取此信息实际上非常困难,Flask-SQLAlchemy 在查询完成时遍历调用堆栈,直到找到与应用程序导入名称匹配的源位置。尽管这是一种非常酷的技术,但它也非常神奇且晦涩难懂。 我还没有看到它在任何其他 Flask 扩展中使用。
def get_debug_queries(): return getattr(_app_ctx_stack.top, 'sqlalchemy_queries', [])
3. 其他特性
系统变量的作用 - 还有两个特别的使用特点
在 Flask 项目的源代码中查看 import_name 参数的其他用法,发现了另外两个值得一提的使用示例。
- 第一点
就是 Blueprint 类,它将 blueprint 的名称作为第一个参数,将 import_name 作为第二个参数。蓝图中参数的使用与寻找蓝图特定资源有关,它的工作方式与应用程序实例中的工作方式相同。
class Blueprint(_PackageBoundObject): import_name = None def __init__(self, name, import_name, ...): _PackageBoundObject.__init__( self, import_name, template_folder, root_path=root_path )
- 第二点
使用 import_name 参数的另一个有趣的地方是为应用程序实例命名。当打印应用程序实例时,将显示应用程序的名称,如下所示。
>>> from flask import Flask >>> app = Flask('foo') >>> app <Flask 'foo'>
这个名称被分配给 Click 命令行界面的主组。如果要将项目的 Flask CLI 作为一个组附加到父 CLI 中,那么这个名称将是你用来访问 Flask 应用程序命令的组名。
@click.command("shell", short_help="Run a shell in the app context.") @with_appcontext def shell_command(): import code from .globals import _app_ctx_stack app = _app_ctx_stack.top.app banner = "Python %s on %snApp: %s [%s]nInstance: %s" % ( sys.version, sys.platform, app.import_name, app.env, app.instance_path, )
4. 参考文档
送人玫瑰,手有余香