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

在 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 你需要把数据作为参数一层层传下去,代码会变得非常臃肿。

python 复制代码
# ❌ 糟糕的写法:参数传递繁琐
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)中把数据存进去,然后在任何地方直接取用。

python 复制代码
# ✅ 优雅的写法:使用 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. 代码示例

python 复制代码
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 为你准备的一个**"请求级背包"**,你在请求开始时把需要的东西放进去,在这个请求处理的任何环节都能伸手拿出来用,请求一结束,背包就自动清空。

相关推荐
颜酱4 小时前
# 02 | 搭骨架:用 LangGraph 编排 12 步工作流(思路)
前端·人工智能·后端
颜酱4 小时前
02 | 搭骨架:用 LangGraph 编排 12 步工作流
前端·人工智能·后端
嘉伟桑5 小时前
Python调用电价API返回JSON示例:分时电价、现货电价和字段解析
后端
武子康5 小时前
Shippy:确定性工具、会话级 Sandbox 与 Live-Data Eval(4 类收敛 + 7 步实现方案 + 6 类评测指标)
前端·人工智能·后端
我叫黑大帅5 小时前
我为什么单一消费者的场景下,要用 Redis List 当消息队列?
redis·后端·面试
AskHarries6 小时前
文件上传系统
后端
止语Lab7 小时前
好的 DX 不等于少写代码——三种语言的摩擦力设计课
后端
吃饱了得干活7 小时前
别再手动解析 LLM 输出了!LangChain 四种结构化输出方案对比
后端·python·langchain
程序员天天困7 小时前
Arthas trace 命令怎么用?一行定位最慢那行代码
jvm·后端
Huiturn7 小时前
GPT 5.6 连续编码 10 小时,纯 Python 啃下 Word 二进制格式——doc2docx 实现拆解
后端