Flask中执行含函数的Python字符串报错NameError求助
问题:Flask类方法中exec定义函数后调用报NameError
我有一段用exec执行Python函数定义字符串的代码,在普通Python脚本(python3 test.py)里能正常运行:
# 包含函数定义的字符串 function_string = "def greet(name):\n print('Hello, ' + name + '!')" # 执行字符串代码 exec(function_string) # 调用函数 name = "Alice" greet(name) # 输出:Hello, Alice!
但把这段代码放到Flask应用的类方法extract_all_delimiters里时,运行报错:
def extract_all_delimiters(self, processed_files_meta_data, set_delimiter_algorithm_fn: str): print(f'extract_all_delimiters()') delimiters = [] exec(set_delimiter_algorithm_fn) # 测试用的函数定义字符串 function_string = "def greet(name):\n print('Hello, ' + name + '!')" # 执行字符串代码 exec(function_string) # 尝试调用函数 name = "Alice" greet(name) # 这里报错:NameError: name 'greet' is not defined
控制台报错信息:
docwise-backend-web-1 | extract_all_delimiters() docwise-backend-web-1 | 192.168.65.1 - - [15/Oct/2023 17:22:27] "POST /api/conversation/create_conversation HTTP/1.1" 500 - docwise-backend-web-1 | Traceback (most recent call last): docwise-backend-web-1 | File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 2548, in __call__ docwise-backend-web-1 | return self.wsgi_app(environ, start_response) docwise-backend-web-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ docwise-backend-web-1 | File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 2528, in wsgi_app docwise-backend-web-1 | response = self.handle_exception(e) docwise-backend-web-1 | ^^^^^^^^^^^^^^^^^^^^^^^^ docwise-backend-web-1 | File "/usr/local/lib/python3.11/site-packages/flask_cors/extension.py", line 176, in wrapped_function docwise-backend-web-1 | return cors_after_request(app.make_response(f(*args, **kwargs))) docwise-backend-web-1 | ^^^^^^^^^^^^^^^^^^^^ docwise-backend-web-1 | File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 2525, in wsgi_app docwise-backend-web-1 | response = self.full_dispatch_request() docwise-backend-web-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ docwise-backend-web-1 | File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 1822, in full_dispatch_request docwise-backend-web-1 | rv = self.handle_user_exception(e) docwise-backend-web-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ docwise-backend-web-1 | File "/usr/local/lib/python3.11/site-packages/flask_cors/extension.py", line 176, in wrapped_function docwise-backend-web-1 | return cors_after_request(app.make_response(f(*args, **kwargs))) docwise-backend-web-1 | ^^^^^^^^^^^^^^^^^^^^ docwise-backend-web-1 | File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 1820, in full_dispatch_request docwise-backend-web-1 | rv = self.dispatch_request() docwise-backend-web-1 | ^^^^^^^^^^^^^^^^^^^^^^^ docwise-backend-web-1 | File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 1796, in dispatch_request docwise-backend-web-1 | return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) docwise-backend-web-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ docwise-backend-web-1 | File "/app/routes/conversation.py", line 11, in create_conversation_route docwise-backend-web-1 | return create_conversation_controller() docwise-backend-web-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ docwise-backend-web-1 | File "/app/controllers/conversation/create_conversation_controller.py", line 160, in create_conversation_controller docwise-backend-web-1 | files_chuncks:List[object] = chunck_text(delimiter_algorithm,files,user_id) docwise-backend-web-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ docwise-backend-web-1 | File "/app/controllers/conversation/create_conversation_controller.py", line 111, in chunck_text docwise-backend-web-1 | extracted_delimiters = delimiter_chuncker.extract_all_delimiters(processed_files_meta_data,delimiter_algorithm['algorithm']) docwise-backend-web-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ docwise-backend-web-1 | File "/app/util/text_chunckers/delimiter.py", line 102, in extract_all_delimiters docwise-backend-web-1 | greet(name) # This will print: Hello, Alice! docwise-backend-web-1 | NameError: name 'greet' is not defined
原因分析
这是Python中exec函数在类方法局部作用域下的特性导致的:
- 在普通脚本的全局作用域中,
exec默认会把定义的函数直接存入全局命名空间,所以后续能直接调用。 - 但在类方法的局部作用域里,Python的函数局部命名空间是在编译阶段就确定的,
exec默认使用的局部作用域对象(通过locals()获取)是只读的,修改后不会同步到当前局部命名空间。这意味着用exec定义的函数不会自动出现在当前方法的局部变量中,自然就找不到greet这个名字。
解决方法
有两种可靠的方式解决这个问题:
方法1:显式指定全局命名空间
把exec的执行结果存入全局命名空间,后续从全局空间调用函数:
def extract_all_delimiters(self, processed_files_meta_data, set_delimiter_algorithm_fn: str): print(f'extract_all_delimiters()') delimiters = [] exec(set_delimiter_algorithm_fn, globals()) function_string = "def greet(name):\n print('Hello, ' + name + '!')" # 指定globals()作为exec的命名空间 exec(function_string, globals()) name = "Alice" greet(name) # 现在可以正常调用
方法2:使用自定义字典作为命名空间
创建一个自定义字典来存储exec执行后的变量,后续从字典中取出函数调用:
def extract_all_delimiters(self, processed_files_meta_data, set_delimiter_algorithm_fn: str): print(f'extract_all_delimiters()') delimiters = [] # 自定义命名空间字典 exec_namespace = {} exec(set_delimiter_algorithm_fn, exec_namespace) function_string = "def greet(name):\n print('Hello, ' + name + '!')" # 把执行结果存入自定义字典 exec(function_string, exec_namespace) name = "Alice" # 从字典中取出函数调用 exec_namespace['greet'](name)
这种方式更安全,不会污染全局命名空间,适合在类方法或函数内部使用。
内容的提问来源于stack exchange,提问作者user17281101
相关产品推荐
相关产品推荐

