Python字符串转列表通用方法:从JSON到eval的安全实践

发布时间:2026/8/11 5:40:56
Python字符串转列表通用方法:从JSON到eval的安全实践 1. 引言在Python编程中我们经常会遇到需要将字符串表示的列表转换为实际列表对象的情况。比如从配置文件读取的字符串[qwen-turbo,qwen-plus,deepseek]或者包含对象实例的字符串[Person(Alice), Person(Bob)]。本文将详细介绍几种通用的转换方法并分析它们的适用场景和安全注意事项。2. 基础场景JSON格式字符串转列表2.1 使用json模块对于标准的JSON格式字符串json.loads()是最安全、最推荐的方法importjson# 示例1字符串列表raw_str[qwen-turbo,qwen-plus,deepseek]lsjson.loads(raw_str)print(ls)# [qwen-turbo, qwen-plus, deepseek]print(type(ls))# class list# 示例2嵌套列表nested_str[[a, b], [c, d]]nested_listjson.loads(nested_str)print(nested_list)# [[a, b], [c, d]]2.2 处理单引号问题JSON标准要求双引号但有时数据可能使用单引号。可以使用str.replace()预处理importjson raw_str[qwen-turbo,qwen-plus,deepseek]# 将单引号替换为双引号json_strraw_str.replace(,)lsjson.loads(json_str)print(ls)# [qwen-turbo, qwen-plus, deepseek]3. 进阶场景包含Python对象的字符串3.1 使用ast.literal_eval()当字符串包含Python字面量表达式时ast.literal_eval()比eval()更安全importast# 示例1基本列表raw_str[qwen-turbo, qwen-plus, deepseek]lsast.literal_eval(raw_str)print(ls)# [qwen-turbo, qwen-plus, deepseek]# 示例2数字列表num_str[1, 2, 3, 4, 5]num_listast.literal_eval(num_str)print(num_list)# [1, 2, 3, 4, 5]# 示例3混合类型mixed_str[1, hello, True, None]mixed_listast.literal_eval(mixed_str)print(mixed_list)# [1, hello, True, None]3.2 处理自定义对象实例对于包含对象实例的字符串需要更复杂的处理importastclassPerson:def__init__(self,name):self.namenamedef__repr__(self):returnfPerson({self.name})def__eq__(self,other):returnisinstance(other,Person)andself.nameother.name# 方法1使用eval仅限可信来源raw_str[Person(Alice), Person(Bob)]# 需要先定义Person类在当前作用域lseval(raw_str,{Person:Person})print(ls)# [Person(Alice), Person(Bob)]# 方法2安全解析并重建defparse_object_list(obj_str):安全解析对象列表字符串# 使用ast解析为ASTtreeast.parse(obj_str,modeeval)# 遍历AST节点提取构造信息objects[]fornodeinast.walk(tree):ifisinstance(node,ast.Call)andisinstance(node.func,ast.Name):ifnode.func.idPerson:# 提取参数args[]forarginnode.args:ifisinstance(arg,ast.Constant):args.append(arg.value)objects.append(Person(*args))returnobjects raw_str[Person(Alice), Person(Bob)]lsparse_object_list(raw_str)print(ls)# [Person(Alice), Person(Bob)]4. 通用转换函数4.1 智能转换函数下面是一个通用的智能转换函数可以处理多种格式importjsonimportastimportredefsmart_str_to_list(raw_str,safe_modeTrue): 将字符串转换为列表的通用函数 参数: raw_str: 输入字符串 safe_mode: 是否启用安全模式禁用eval 返回: 转换后的列表 # 去除首尾空白raw_strraw_str.strip()ifnotraw_str:return[]# 尝试JSON解析最安全try:# 处理单引号情况json_strraw_str.replace(,)returnjson.loads(json_str)exceptjson.JSONDecodeError:pass# 尝试ast.literal_eval相对安全try:returnast.literal_eval(raw_str)except(SyntaxError,ValueError):pass# 如果允许且必要尝试eval仅限可信数据ifnotsafe_mode:try:returneval(raw_str)except:pass# 最后尝试正则分割# 匹配引号内的内容或非空白字符patternr[\]([^\]*)[\]|([^,\s\[\]])matchesre.findall(pattern,raw_str)result[]formatchinmatches:# match[0]是引号内的内容match[1]是非引号内容itemmatch[0]ifmatch[0]elsematch[1]ifitem:# 尝试转换为数字try:if.initem:result.append(float(item))else:result.append(int(item))exceptValueError:result.append(item)returnresult# 测试各种格式test_cases[[qwen-turbo,qwen-plus,deepseek],[qwen-turbo,qwen-plus,deepseek],[1, 2, 3, 4, 5],[true, false, null],# JSON布尔值和null[a, b, c],apple, banana, cherry,]fortestintest_cases:resultsmart_str_to_list(test)print(f输入:{test})print(f输出:{result})print(f类型:{type(result)})print(-*40)4.2 带类型推断的转换defstr_to_list_with_types(raw_str): 将字符串转换为列表并自动推断元素类型 try:# 先尝试JSONresultjson.loads(raw_str.replace(,))except:try:# 再尝试literal_evalresultast.literal_eval(raw_str)except:# 最后使用简单分割result[item.strip()foriteminraw_str.strip([]).split(,)]# 类型推断和转换typed_result[]foriteminresult:ifisinstance(item,(int,float,bool,type(None))):typed_result.append(item)elifisinstance(item,str):# 尝试转换为数字try:if.initem:typed_result.append(float(item))else:typed_result.append(int(item))exceptValueError:# 尝试转换为布尔值ifitem.lower()true:typed_result.append(True)elifitem.lower()false:typed_result.append(False)elifitem.lower()nulloritem.lower()none:typed_result.append(None)else:typed_result.append(item)else:typed_result.append(item)returntyped_result# 测试test_str[1, 2.5, true, false, null, hello]resultstr_to_list_with_types(test_str)print(f输入:{test_str})print(f输出:{result})print(f类型:{[type(x)forxinresult]})5. 安全注意事项5.1 eval的安全风险# 危险示例永远不要对不可信数据使用evalmalicious_str__import__(os).system(rm -rf /)# 危险# result eval(malicious_str) # 千万不要执行# 安全替代方案defsafe_eval(expression,allowed_namesNone):受限的eval实现ifallowed_namesisNone:allowed_names{}# 编译时检查try:codecompile(expression,string,eval)exceptSyntaxError:raiseValueError(Invalid expression)# 检查允许的节点类型fornodeinast.walk(ast.parse(expression,modeeval)):ifisinstance(node,(ast.Import,ast.ImportFrom,ast.Call)):# 禁止导入和函数调用raiseValueError(Import and function calls are not allowed)returneval(code,{__builtins__:{}},allowed_names)5.2 最佳实践优先使用json.loads()处理JSON格式数据次选ast.literal_eval()处理Python字面量避免使用eval()除非完全控制数据来源验证输入数据使用白名单验证错误处理始终使用try-exceptdefsafe_str_to_list(raw_str,allowed_classesNone):安全地将字符串转换为列表ifallowed_classesisNone:allowed_classes{}# 输入验证ifnotisinstance(raw_str,str):raiseTypeError(Input must be a string)# 长度限制iflen(raw_str)10000:raiseValueError(Input string too long)try:# 尝试JSONreturnjson.loads(raw_str)exceptjson.JSONDecodeError:try:# 尝试literal_evalreturnast.literal_eval(raw_str)except(SyntaxError,ValueError):# 自定义对象处理受限ifallowed_classes:returneval(raw_str,{__builtins__:{}},allowed_classes)else:raiseValueError(Cannot safely parse the string)6. 实际应用示例6.1 配置文件解析importconfigparserimportjsonclassConfigLoader:def__init__(self,config_file):self.configconfigparser.ConfigParser()self.config.read(config_file)defget_list(self,section,key,defaultNone):从配置文件中获取列表try:valueself.config.get(section,key)returnsmart_str_to_list(value)except(configparser.NoSectionError,configparser.NoOptionError):returndefaultifdefaultisnotNoneelse[]# 配置文件内容示例# [models]# active_models [qwen-turbo, qwen-plus, deepseek]# batch_sizes [32, 64, 128]6.2 API响应处理importrequestsimportjsondefparse_api_response(response_text):解析API返回的字符串列表try:datajson.loads(response_text)# 如果API返回的是字符串形式的列表ifisinstance(data,dict)andmodelsindata:models_strdata[models]ifisinstance(models_str,str):returnsmart_str_to_list(models_str)returnmodels_strreturndataexceptjson.JSONDecodeError:# 尝试其他格式returnsmart_str_to_list(response_text)# 模拟API响应api_response{models: [\\qwen-turbo\\, \\qwen-plus\\, \\deepseek\\]}resultparse_api_response(api_response)print(f解析结果:{result})7. 性能比较importtimeitimportjsonimportast test_str[qwen-turbo,qwen-plus,deepseek,model-a,model-b]*100# 性能测试deftest_json():returnjson.loads(test_str)deftest_ast():returnast.literal_eval(test_str)deftest_eval():returneval(test_str)# 运行测试json_timetimeit.timeit(test_json,number1000)ast_timetimeit.timeit(test_ast,number1000)eval_timetimeit.timeit(test_eval,number1000)print(fjson.loads:{json_time:.6f}seconds)print(fast.literal_eval:{ast_time:.6f}seconds)print(feval:{eval_time:.6f}seconds)8. 总结本文介绍了多种将字符串转换为列表的方法json.loads()最安全适合JSON格式数据ast.literal_eval()相对安全适合Python字面量eval()功能最强但最危险仅限可信数据自定义解析函数灵活可控适合特定需求选择方法时需要考虑数据来源是否可信字符串格式是否规范是否需要处理自定义对象性能要求对于大多数场景推荐使用json.loads()或ast.literal_eval()避免使用eval()处理不可信数据。对于包含自定义对象的字符串建议使用安全的AST解析方法或限制eval的执行环境。