翻译
Why do we pass__name__
to the Flask class?
当你学习 Flask 时,你被告知通过将 __name__
作为第一个参数传递给 Flask 类来创建 Flask 应用程序实例大多数开发人员这样做时不加思考,也不知道它能达到什么效果。
在本文中,我们将深入研究 Flask(__name__
)。最后,你不仅会完全理解这个模式,而且还会知道何时抛弃它并传递其他值。
__name__
是什么?
Python 将 __name__
变量设置为模块名称,因此该变量的值将根据使用它的 Python 源文件而有所不同。
例如,在应用程序顶级目录中名为 test.py 的模块中,__name__
的值为 test。如果 test.py 模块位于一个名为 my_package
的 Python 包中,那么 __name__
的值就是 my_package.test。
__name__
的值有两个特殊的例外:
- 在
__init__.py
包构造模块中,__name__
的值是包名而不是__init__.py
。 例如,在 my_package/__init__.py 中,__name__
的值就是 my_package。 在应用程序的主模块(运行 Python 解释器的文件)中,
__name__
的值是特殊值__main__
。Flask 文档中的
__name__
如果你阅读 Flask 文档,
Flask
类的第一个参数称为 import_name。 它被描述为“应用程序包的名称”。 该文档建议你“通常”通过为此参数传递__name__
来创建 Flask 实例,而无需详细说明原因。
在标题为“关于第一个参数”的小节中,有更多关于 import_name 参数用途的信息,列出了它的三个不同用途:
- 查找文件系统上的资源
- 被扩展使用以改进调试信息
- 更多 (???)
很令人困惑,对吧? 让我们一个一个来看看。
寻找资源
乍一看,这实际上更容易理解。 本上下文中的术语“资源”是指应用程序所需的附加文件,例如静态文件和模板文件。 你有没有想过 Flask 如何知道去哪里寻找这些文件?
它的工作方式如下。 Flask 接受作为 import_name
传递的参数,它是导入包的名称,并尝试使用它通过查找具有该名称的模块对象来确定应用程序的根路径。 一旦知道了这个路径,它就会拼接静态和模板目录名称,这就是它获取这些文件的地方。
在 flask/helpers.py
模块中有一个名为 get_root_path()
的函数,Flask 使用它来获取应用程序的根目录。 这个函数完成了导航 Python 导入系统以定位名称为import_name
参数的模块的所有脏活。
让我们快速浏览一下 get_root_path() 函数。 对于下面的测试,我将使用 Flask Mega-Tutorial 中的 microblog 应用程序。 正确安装此应用程序后,我将启动 Python 会话并使用几个不同的值调用 get_root_path() 函数以查看结果:
>>> 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 threading package
>>> get_root_path('threading')
'/home/miguel/.pyenv/versions/3.8.6/lib/python3.8'
# 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'
# the app.api.auth.py module
>>> get_root_path('app.api.auth')
'/Users/mgrinberg/Documents/dev/python/microblog/app/api'
从这些例子中,我们可以推导出 Flask 用来确定根路径的规则:
- 如果调用
Flask()
将包的名称作为参数传递,那么应用程序的根路径就是包所在的目录。 - 如果调用
Flask()
将模块的名称作为参数传递,那么应用程序的根路径就是该模块所在的包的目录。
import_name
参数还有第二种用法。 Flask 有一个不起眼的功能,称为instance folders,这是一个特殊的文件夹,可以存储不受源代码控制的配置文件。 确定应用程序实例文件夹位置的方法是一样的,使用 import_name
查找根路径,然后向其添加一个实例子目录。
我应该注意到,默认的根路径和实例路径都可以在 Flask 构造函数中用 root_path
和 instance_path
参数覆盖。
改进 Flask 扩展中的调试信息
这个很难弄清楚。 我发现唯一一个使用 import_name
参数的 Flask 扩展是 Flask-SQLAlchemy。 该扩展有一个 get_debug_queries()函数,用于收集和记录在请求生命周期内发出的所有查询。 记录的属性之一是发出查询在应用程序源代码中的位置。 获取这些信息实际上非常困难,Flask-SQLAlchemy 在查询完成时会沿着调用堆栈向上走,直到找到与应用程序的 import_name
匹配的源位置。
虽然这是一种非常酷的技术,但它也非常神奇和晦涩。 我还没有看到它在任何其他 Flask 扩展中使用。
更多
我查看了 Flask 源代码以了解 import_name
参数的其他用法,并发现另外两个值得一提的案例。
一个是 Blueprint
类,它将蓝图名称作为第一个参数,将 import_name
作为第二个参数。 参数在蓝图中的使用与查找蓝图特定资源有关,它的工作方式与应用程序实例中的相同。
使用 import_name参数的另一个有趣的地方是为应用程序实例命名。 打印应用程序实例时会显示应用程序的名称:
>>> from flask import Flask
>>> app = Flask('foo')
>>> app
<Flask 'foo'>
此名称分配给 Click 命令行接口的maingroup 。 如果将项目的 Flask CLI 作为一个 group 附加到parent CLI 中,则此名称将是用于访问 Flask 应用程序命令的 group 名称。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。