Python中为何列表推导式内的eval无法识别self,而for循环内的eval却可以?
Python中为何列表推导式内的eval无法识别self,而for循环内的eval却可以?
这个问题其实涉及到Python中列表推导式和普通for循环的作用域差异,还有eval函数查找变量的规则,我来给你掰扯清楚:
核心原因:作用域的不同
Python 3开始,列表推导式会创建一个独立的局部作用域,就像一个隐式的小型匿名函数。在你setUp方法的列表推导式里调用eval时,eval默认会使用推导式自身的局部作用域,而这个作用域里并没有self变量——self是属于setUp方法的作用域的,推导式的作用域无法直接访问到它,所以直接写self.foo.{attr}就会抛出NameError。这时候你手动传入{"self": self}作为eval的globals参数,相当于给eval提供了一个能找到self的环境,它才能正确解析表达式。
而test_all_wrapped_calls_are_logged里的普通for循环是直接在测试方法的作用域内执行的,没有创建新的局部作用域。这时候调用eval,它会直接使用当前测试方法的局部作用域,里面本来就存在self变量,所以不需要额外传参数,eval能直接找到self。
一个更优的替代方案
其实完全没必要用eval来做这些操作,eval不仅有安全风险,还容易碰到这类作用域的坑。你可以用Python内置的getattr函数来替代,代码会更清晰安全:
修改setUp里的列表推导式:
self.wrapped_funcs = [ attr for attr in dir(self.foo) if isinstance(getattr(self.foo, attr), types.FunctionType) ]
修改测试方法里的调用逻辑:
def test_all_wrapped_calls_are_logged(self): for func in self.wrapped_funcs: with patch("logging.info") as mocked_logger: getattr(self.foo, func)('bar') mocked_logger.assert_called_once()
这样既解决了作用域的问题,代码可读性和安全性也提升了不少。
备注:内容来源于stack exchange,提问作者user15264046
相关产品推荐
相关产品推荐

