在 Flask 框架中,g 是一个全局对象(Global Object),但它并不是传统意义上的“全局变量”(即不是模块级的全局变量)。
它的核心作用是:在一次请求(Request)的生命周期内,存储和共享数据。
1. 核心特性
-
请求隔离(Request-Specific) :这是
g最重要的特性。每个请求都有自己独立的g对象。- 用户 A 发起请求,
g.user = "Alice"。 - 用户 B 同时发起请求,
g.user = "Bob"。 - 这两个
g互不干扰。即使在高并发下,用户 A 也绝对不会读到用户 B 存在g里的数据。
- 用户 A 发起请求,
-
生命周期:
g对象在请求开始时创建,在请求结束时自动销毁(或重置)。 -
用途:用于在同一个请求的不同函数之间传递数据,避免通过参数层层传递。
2. 为什么要用 g?(解决什么问题)
在 Web 开发中,我们经常需要在视图函数、数据库操作函数、权限验证装饰器等不同地方访问相同的数据(例如:当前登录的用户对象、数据库连接、租户ID等)。
如果不使用 g: 你需要把数据作为参数一层层传下去,代码会变得非常臃肿。
# ❌ 糟糕的写法:参数传递繁琐
def get_user_profile(user, db_conn):
return query_db(db_conn, user.id)
def check_permission(user, db_conn):
if not is_admin(user, db_conn):
abort(403)
@app.route('/profile')
def profile():
user = get_current_user() # 获取用户
db = get_db_connection() # 获取数据库连接
check_permission(user, db) # 传递
data = get_user_profile(user, db) # 再次传递
return render_template('profile.html', user=data)
使用 g 后: 你可以在请求开始的钩子函数(如 before_request)中把数据存进去,然后在任何地方直接取用。
# ✅ 优雅的写法:使用 g 共享数据
from flask import g, request
@app.before_request
def load_user_and_db():
# 在请求开始时,初始化数据并存入 g
g.user = get_current_user()
g.db = get_db_connection()
def get_user_profile():
# 直接使用 g.user 和 g.db,无需参数传递
return query_db(g.db, g.user.id)
def check_permission():
if not is_admin(g.user, g.db):
abort(403)
@app.route('/profile')
def profile():
check_permission() # 内部自动读取 g 中的数据
data = get_user_profile()
return render_template('profile.html', user=data)
3. 常见应用场景
- 存储当前登录用户: 在认证装饰器或
before_request中解析 Token,将用户对象存入g.current_user,后续视图函数直接使用。 - 管理数据库连接: 在请求开始时建立数据库连接存入
g.db,在请求结束时(teardown_request)关闭连接。这样可以确保每个请求复用连接,且不会泄露。 - 多租户/上下文信息: 根据域名或 Header 解析出的租户 ID、语言设置等,存入
g供后续逻辑使用。 - 缓存请求级计算结果: 如果在同一个请求中多次需要某个耗时的计算结果,可以算一次存入
g,下次直接取。
4. 代码示例
from flask import Flask, g, request, jsonify
app = Flask(__name__)
# 1. 请求开始前执行
@app.before_request
def before_request():
# 模拟从 Header 获取用户 ID
user_id = request.headers.get('X-User-ID')
if user_id:
# 将用户信息存入 g,整个请求期间可用
g.user = {"id": user_id, "name": f"User_{user_id}"}
else:
g.user = None
# 模拟打开数据库连接
g.db_connection = "DB_CONNECTION_OBJECT"
print(f"请求开始: 初始化了 g 对象")
# 2. 请求结束后执行(清理资源)
@app.teardown_request
def teardown_request(exception):
if hasattr(g, 'db_connection'):
# 关闭数据库连接
print(f"请求结束: 关闭了 g.db_connection")
g.db_connection = None
# 3. 辅助函数:任意地方都可以访问 g
def get_current_user_info():
if not hasattr(g, 'user') or g.user is None:
return "未登录"
return f"欢迎, {g.user['name']}"
@app.route('/dashboard')
def dashboard():
# 直接使用 g 中的数据,不需要函数传参
msg = get_current_user_info()
# 也可以直接访问 g.db_connection 进行查询
# data = query(g.db_connection, ...)
return jsonify({"message": msg})
if __name__ == '__main__':
app.run(debug=True)
5. 重要注意事项
- 不要跨请求使用:千万不要试图在后台线程或其他请求中访问
g。它只在当前请求的上下文中有效。如果在后台线程中使用,会抛出RuntimeError: Working outside of request context。 - 不是全局状态:不要用它来存储应用级别的全局配置(那是
app.config的作用)或用户会话数据(那是session的作用)。 - 属性动态性:
g是一个普通的对象,你可以随意给它添加属性(如g.my_var = 123),Flask 不会限制你。
总结
| 特性 | 说明 |
|---|---|
| 全称 | Global (但在请求上下文中) |
| 作用域 | 单次请求内有效,不同请求互不干扰 |
| 主要用途 | 在请求的不同函数间共享数据(用户、DB连接、配置等) |
| 替代方案 | 如果没有 g,就需要通过函数参数层层传递,代码耦合度高 |
| 底层原理 | 基于 Flask 的 Context Local (上下文局部变量) 实现 |
简而言之,g 就是 Flask 为你准备的一个**“请求级背包”**,你在请求开始时把需要的东西放进去,在这个请求处理的任何环节都能伸手拿出来用,请求一结束,背包就自动清空。