如何在Python中使用exec()修改变量?函数内变量未更新问题
解决exec()无法修改外部变量passed的问题
你的代码中exec()无法修改外部的passed变量,核心原因是Python的作用域规则限制:在嵌套函数pass_check里,passed被视为局部变量,但exec()默认使用的命名空间不会直接修改这个局部变量——exec里的赋值操作只会修改自身临时命名空间中的passed,而不会同步到外部函数的passed变量上。
下面是几种可行的解决方法:
方法1:给exec传递显式的局部命名空间
通过给exec()传递第三个参数作为局部命名空间字典,手动同步变量值:
def get_random_persons_by_attributes(self, attributes, k): def pass_check(person): passed = True local_vars = locals() for att in attributes: exec(f"if not person.{att['attribute']} {att['operator']} {att['value']}: passed = False", globals(), local_vars) # 从命名空间字典中同步修改后的值 passed = local_vars['passed'] return passed print(pass_check(self.residents[0]))
方法2:替换exec为eval(更简洁)
避免使用exec,改用eval直接计算条件表达式,逻辑更清晰:
def get_random_persons_by_attributes(self, attributes, k): def pass_check(person): passed = True for att in attributes: # 计算条件是否成立 condition = eval(f"person.{att['attribute']} {att['operator']} {att['value']}") if not condition: passed = False # 提前跳出循环优化性能 break return passed print(pass_check(self.residents[0]))
方法3:完全避免exec/eval(最安全)
如果担心exec/eval的安全风险(比如传入恶意的属性字符串),可以通过解析属性路径+运算符映射的方式实现:
# 定义运算符映射字典 operator_map = { '==': lambda x, y: x == y, '!=': lambda x, y: x != y, '>': lambda x, y: x > y, '<': lambda x, y: x < y, '>=': lambda x, y: x >= y, '<=': lambda x, y: x <= y, } def get_random_persons_by_attributes(self, attributes, k): def pass_check(person): passed = True for att in attributes: # 解析嵌套属性路径(比如"age[\"age\"]") attr_str = att['attribute'] current_val = person # 处理点分隔的属性 for part in attr_str.split('.'): if '[' in part: # 处理索引访问,比如age["age"] attr_name, index_part = part.split('[', 1) index = index_part.strip(']') # 去除引号(如果是字符串索引) if index.startswith(('"', "'")) and index.endswith(('"', "'")): index = index[1:-1] # 获取属性后再取索引 current_val = getattr(current_val, attr_name)[index] else: # 直接获取属性 current_val = getattr(current_val, part) # 获取对应的运算函数 op_func = operator_map.get(att['operator']) if not op_func: raise ValueError(f"不支持的运算符:{att['operator']}") # 判断条件 if not op_func(current_val, att['value']): passed = False break return passed print(pass_check(self.residents[0]))
这种方法完全避免了动态执行字符串的风险,同时也不会出现作用域相关的问题,是更推荐的实现方式。
内容的提问来源于stack exchange,提问作者HerrLehrer99
相关产品推荐
相关产品推荐

