为何自定义上下文管理器with块内return会退出__enter__函数
问题解答:上下文管理器
__enter__中return的行为 核心原因:return的本质是终止当前函数
不管你在多少层嵌套代码块(比如with、if、for)里用return,它的作用都是立刻终止当前所在的函数,并返回指定值。在你的代码里,return len(self.response.content)写在self.session.get(...)的嵌套with块中,但这个return属于__enter__函数,所以执行到这行时,会直接结束整个__enter__函数,自然不会跑后面的print("I would expect...")这些代码。
另外,上下文管理器的__enter__方法的返回值会绑定到with ... as manager里的manager变量,这也是为什么你的manager最终拿到的是响应内容长度的原因。
修正方案:调整return的位置
如果想执行完嵌套with块的登录逻辑后,继续跑__enter__后续的代码,得把return移到嵌套with块外面,示例代码如下:
import requests class LoginLogoutContextManager: def __enter__(self): print("**entered __enter__") print("Logging in...") self.url = "http://www.google.com" self.session = requests.Session() response_length = 0 with self.session.get(self.url) as self.response: print("Start Login attempt (inside WITH but before RETURN)") response_length = len(self.response.content) print("Login complete (inside WITH after assigning value)") # 现在会执行这里的代码了 print("Now we're back to __enter__ code after nested WITH") print("!!exiting __enter__") # 最后再返回需要绑定给manager的值 return response_length def __exit__(self, exc_type, exc_value, exc_tb): print("**entered __exit__") print("Logging OUT...") # 这里可以添加实际的登出逻辑,比如调用登出API print("!!exiting __exit__") with LoginLogoutContextManager() as manager: print("Cool now the Login/Logout API calls are handled for me") print(f"Login attempt response length: {manager}")
修正后的运行输出
**entered __enter__ Logging in... Start Login attempt (inside WITH but before RETURN) Login complete (inside WITH after assigning value) Now we're back to __enter__ code after nested WITH !!exiting __enter__ Cool now the Login/Logout API calls are handled for me Login attempt response length: 14126 **entered __exit__ Logging OUT... !!exiting __exit__
额外提示:贴合场景的上下文管理器写法
你的需求是用上下文管理器处理登录/登出,更合理的做法是把session对象作为__enter__的返回值,这样在with块里能直接用这个session执行后续API操作:
import requests class LoginLogoutContextManager: def __enter__(self): print("**entered __enter__") print("Logging in...") self.url = "http://www.google.com" self.session = requests.Session() # 模拟登录:实际这里应该调用登录API,比如post登录接口 with self.session.get(self.url) as response: print("Login attempt completed") print("!!exiting __enter__") # 返回session,方便后续操作 return self.session def __exit__(self, exc_type, exc_value, exc_tb): print("**entered __exit__") print("Logging OUT...") # 模拟登出:实际调用登出API # self.session.post(logout_url) self.session.close() print("!!exiting __exit__") with LoginLogoutContextManager() as session: print("Cool now the Login/Logout API calls are handled for me") # 使用session执行其他API操作 # response = session.get(other_api_url) print("Finished other API operations")
内容的提问来源于stack exchange,提问作者Jones Smith
相关产品推荐
相关产品推荐

