【python知识点-Flask中的g对象】

4 阅读4分钟

在 Flask 框架中,g 是一个全局对象(Global Object),但它并不是传统意义上的“全局变量”(即不是模块级的全局变量)。

它的核心作用是:在一次请求(Request)的生命周期内,存储和共享数据。

1. 核心特性

  • 请求隔离(Request-Specific) :这是 g 最重要的特性。每个请求都有自己独立的 g 对象。

    • 用户 A 发起请求,g.user = "Alice"
    • 用户 B 同时发起请求,g.user = "Bob"
    • 这两个 g 互不干扰。即使在高并发下,用户 A 也绝对不会读到用户 B 存在 g 里的数据。
  • 生命周期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. 常见应用场景

  1. 存储当前登录用户: 在认证装饰器或 before_request 中解析 Token,将用户对象存入 g.current_user,后续视图函数直接使用。
  2. 管理数据库连接: 在请求开始时建立数据库连接存入 g.db,在请求结束时(teardown_request)关闭连接。这样可以确保每个请求复用连接,且不会泄露。
  3. 多租户/上下文信息: 根据域名或 Header 解析出的租户 ID、语言设置等,存入 g 供后续逻辑使用。
  4. 缓存请求级计算结果: 如果在同一个请求中多次需要某个耗时的计算结果,可以算一次存入 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 为你准备的一个**“请求级背包”**,你在请求开始时把需要的东西放进去,在这个请求处理的任何环节都能伸手拿出来用,请求一结束,背包就自动清空。