Python exec()执行PSQL表取出的赋值语句触发NameError问题
问题根因
这是Python exec() 的作用域规则导致的,和你从PostgreSQL读取的字符串内容没有关系。
你在交互式Python Shell里直接执行exec(res)能正常生成keyword变量,是因为在模块顶层(全局作用域)调用exec()且不手动传入命名空间参数时,exec()会默认把执行过程中赋值的变量绑定到当前全局作用域,后续代码自然能直接访问到。
但只要你把这段数据库读取+exec执行的逻辑写在函数内部,情况就不一样了:此时exec()默认会把赋值产生的变量存入当前函数的本地作用域,而Python函数的本地变量绑定关系是在代码编译阶段就确定的,你在函数里直接写print(keyword)时,解释器会判定keyword是未定义的本地变量,不会读取exec()动态写入的作用域内容,直接抛出NameError。
你测试id=3的打印指令能正常执行也很好理解:print('PRINT FOR DEMO')只是调用内置函数,不需要在当前作用域创建新变量,完全不受本地变量预绑定规则的影响。
修复方法
两种方案按需选即可:
- 方案1:显式传入全局命名空间,强制动态变量绑定到全局作用域
conn = connect_db(host,database,user,password) cur = conn.cursor() query = 'SELECT commands FROM table WHERE flag_activ = TRUE' cur.execute(query) res = cur.fetchone()[0] # 显式指定exec使用全局命名空间 exec(res, globals()) print(keyword) # 正常输出 miscellaneous
- 方案2:用独立字典接收exec生成的变量,不污染全局作用域(更安全,推荐)
因为你执行的是数据库中存储的动态指令,本身就应该做执行隔离,避免恶意代码篡改全局变量:
conn = connect_db(host,database,user,password) cur = conn.cursor() query = 'SELECT commands FROM table WHERE flag_activ = TRUE' cur.execute(query) res = cur.fetchone()[0] # 独立命名空间承接动态执行产生的变量 dynamic_ns = {} exec(res, dynamic_ns) keyword = dynamic_ns['keyword'] print(keyword) # 正常输出 miscellaneous
最小复现验证
你可以跑下面这段和数据库操作完全无关的代码,100%复现你遇到的报错,证明问题和PSQL驱动、返回值内容无关:
def run_dynamic_code(): code_str = "keyword = 'miscellaneous'" exec(code_str) print(keyword) # 这里就会抛出NameError: name 'keyword' is not defined run_dynamic_code()
把exec调用换成上面两种修复写法,报错就会消失。
注意:执行数据库中存储的可执行代码存在极高安全风险,如果commands字段内容可被非信任来源篡改,会直接导致远程代码执行漏洞,生产环境尽量避免使用这类实现。
内容的提问来源于stack exchange,提问作者junsuzuki
相关产品推荐
相关产品推荐

