【发布时间】:2020-02-17 21:53:45
【问题描述】:
在 Python 3 中使用 contextmanager(我还没有测试过 Python 2)对于在 with 子句的范围内声明的变量有一些奇怪的行为。
在我看来,这些变量的行为类似于“远处的怪异动作”,因为只有在观察到它们时它们才似乎存在(来自非物理工程师的笑话)。
在上下文管理器范围内,在屈服点之后:
如果你……
- 打印出
locals()
那么变量确实不存在。
但如果你:
- 打印出
locals(); - 对托管范围的变量做任何事情
那么变量就存在了!!
看这个例子:
from contextlib import contextmanager
def groucho():
@contextmanager
def groucho_manager(**kwargs):
yield
print("groucho_manager locals", locals())
a
with groucho_manager(lolcat=10):
a = 50
def harpo():
@contextmanager
def harpo_manager(**kwargs):
yield
print("harpo_manager locals", locals())
with harpo_manager(lolcat=10):
b = 100
groucho()
harpo()
输出:
groucho_manager locals {'kwargs': {'lolcat': 10}, 'a': 50}
harpo_manager locals {'kwargs': {'lolcat': 10}}
可能与Python class inheritance - spooky action有关,但我不确定。
【问题讨论】:
-
封闭函数中的变量只有在内部函数在编译时实际上引用了变量时才可用于内部函数。此类变量的实现比普通的局部变量要复杂得多(因为它们可以比直接包含它们的函数寿命更长);除非明确需要,否则 Python 不会做这些额外的工作。上下文管理器与此无关,它是嵌套函数定义的一般属性。
-
或者换句话说:
locals()只返回函数实际引用的变量,不是它可能引用的每个外部变量。 -
@jasonharper 谢谢解答。他们是否在测试变量 "potentially existing in locals" 的存在性?我的意思是:
getattr(locals(), 'var_name', default_value)不起作用,因为在编译时没有检测到var_name那么使用什么呢?我看到的唯一解决方案是try: ... except:...子句,但在这种情况下看起来很讨厌 -
顺便说一句,我有一个 (可能是邪恶的,但在我看来是合法的) 这样做的原因:
with log_request(args):\n response=send_request(args)在这里,如果log_request可以维持存在可能会很好response,所以它可以同时记录request_args和response对象 -
上下文管理器无法访问在其控制下运行的代码的局部变量。您只能在第一个示例中看到
a,因为它是嵌套在同一范围内的函数,而不是因为它是上下文管理器。
标签: python contextmanager