如果您在 try 之前将 breakpoint() 放入依赖项中并使用 w 选项查看调用树,您可以看到生成器类型函数 next() 在 contextlib 模块代码中被调用:
[0] c:\users\homeuser\.pyenv\pyenv-win\versions\3.7.9\lib\threading.py(890)_bootstrap()
-> self._bootstrap_inner()
[1] c:\users\homeuser\.pyenv\pyenv-win\versions\3.7.9\lib\threading.py(926)_bootstrap_inner()
-> self.run()
[2] c:\users\homeuser\.pyenv\pyenv-win\versions\3.7.9\lib\threading.py(870)run()
-> self._target(*self._args, **self._kwargs)
[3] c:\users\homeuser\.pyenv\pyenv-win\versions\3.7.9\lib\concurrent\futures\thread.py(80)_worker()
-> work_item.run()
[4] c:\users\homeuser\.pyenv\pyenv-win\versions\3.7.9\lib\concurrent\futures\thread.py(57)run()
-> result = self.fn(*self.args, **self.kwargs)
[5] c:\users\homeuser\.pyenv\pyenv-win\versions\3.7.9\lib\contextlib.py(112)__enter__()
-> return next(self.gen)
请注意,这个next() 调用是在__enter__ 特殊函数内完成的,上下文管理器在使用with 关键字打开它时会使用该函数。现在,如果继续执行yield 语句并等到您的断点将进入finally 块,您可以再次使用w 查看调用树:
[0] c:\users\homeuser\.pyenv\pyenv-win\versions\3.7.9\lib\threading.py(890)_bootstrap()
-> self._bootstrap_inner()
[1] c:\users\homeuser\.pyenv\pyenv-win\versions\3.7.9\lib\threading.py(926)_bootstrap_inner()
-> self.run()
[2] c:\users\homeuser\.pyenv\pyenv-win\versions\3.7.9\lib\threading.py(870)run()
-> self._target(*self._args, **self._kwargs)
[3] c:\users\homeuser\.pyenv\pyenv-win\versions\3.7.9\lib\concurrent\futures\thread.py(80)_worker()
-> work_item.run()
[4] c:\users\homeuser\.pyenv\pyenv-win\versions\3.7.9\lib\concurrent\futures\thread.py(57)run()
-> result = self.fn(*self.args, **self.kwargs)
[5] c:\users\homeuser\.pyenv\pyenv-win\versions\3.7.9\lib\contextlib.py(119)__exit__()
-> next(self.gen)
现在您会注意到,在退出上下文管理器对象时,您的生成器函数上调用了 next()。
暂停是对的,但功能状态也会被记住。因此,当__enter__ 中的第一个next() 被调用时,它会生成您的会话对象,然后第二个next() 调用会恢复函数执行,从而可以进入finally 块内。
我认为@contextmanager 装饰器在 FastAPI 实现中的某个地方使用,它处理依赖关系,因此可以在Depends 中使用生成器。
查看this 文章,该文章很好地解释了 Python 生成器的工作原理。