我们如何 "associate" 一个 Python 上下文管理器来处理出现在其块中的变量?
How can we "associate" a Python context manager to the variables appearing in its block?
据我了解,上下文管理器在 Python 中用于定义对象的初始化和完成代码片段(__enter__
和 __exit__
)。
但是,在 tutorial for PyMC3 中,他们显示了以下上下文管理器示例:
basic_model = pm.Model()
with basic_model:
# Priors for unknown model parameters
alpha = pm.Normal('alpha', mu=0, sd=10)
beta = pm.Normal('beta', mu=0, sd=10, shape=2)
sigma = pm.HalfNormal('sigma', sd=1)
# Expected value of outcome
mu = alpha + beta[0]*X1 + beta[1]*X2
# Likelihood (sampling distribution) of observations
Y_obs = pm.Normal('Y_obs', mu=mu, sd=sigma, observed=Y)
并提到这具有将变量 alpha
、beta
、sigma
、mu
和 Y_obs
关联到模型 basic_model.
我想了解这种机制是如何运作的。在我发现的 explanations of 上下文管理器中,我没有看到任何暗示上下文块中定义的变量或对象如何以某种方式 "associated" 进入上下文管理器的提示。库 (PyMC3) 似乎可以以某种方式访问 "current" 上下文管理器,因此它可以在幕后将每个新创建的语句关联到它。但是库如何访问上下文管理器?
我不知道在这个特定情况下它是如何工作的,但通常你会使用一些 'behind the scenes magic':
class Parent:
def __init__(self):
self.active_child = None
def ContextManager(self):
return Child(self)
def Attribute(self):
return self.active_child.Attribute()
class Child:
def __init__(self,parent):
self.parent = parent
def __enter__(self):
self.parent.active_child = self
def __exit__(self, exc_type, exc_val, exc_tb):
self.parent.active_child = None
def Attribute(self):
print("Called Attribute of child")
使用此代码:
p = Parent()
with p.ContextManager():
attr = p.Attribute()
将产生以下输出:
Called Attribute of child
PyMC3 通过在 Context
class 中维护 thread local variable 作为 class 变量 来做到这一点。 Model
s 继承自 Context
.
每次您在模型上调用 with
时,当前模型都会被推送到特定于线程的上下文堆栈中。因此,堆栈的顶部总是指用作上下文管理器的最里面(最近)的模型。
Context
s(因此 Model
s)有一个 .get_context()
class 方法 来获取顶部上下文堆栈。
Distribution
在创建它们时调用 Model.get_context()
以将它们自己与最内层模型相关联。
简而言之:
with model
将 model
压入上下文堆栈。这意味着在 with
块内,type(model).contexts
或 Model.contexts
或 Context.contexts
现在包含 model
作为其最后(最顶层)元素。
Distribution.__init__()
调用 Model.get_context()
(注意大写 M
),其中 returns 上下文堆栈的顶部。在我们的例子中,这是 model
。上下文堆栈是线程本地的(每个线程有一个),但它不是特定于实例的。如果只有一个线程,那么也只有一个上下文堆栈,与模型数量无关。
- 退出上下文管理器时。
model
从上下文堆栈中弹出。
据我了解,上下文管理器在 Python 中用于定义对象的初始化和完成代码片段(__enter__
和 __exit__
)。
但是,在 tutorial for PyMC3 中,他们显示了以下上下文管理器示例:
basic_model = pm.Model()
with basic_model:
# Priors for unknown model parameters
alpha = pm.Normal('alpha', mu=0, sd=10)
beta = pm.Normal('beta', mu=0, sd=10, shape=2)
sigma = pm.HalfNormal('sigma', sd=1)
# Expected value of outcome
mu = alpha + beta[0]*X1 + beta[1]*X2
# Likelihood (sampling distribution) of observations
Y_obs = pm.Normal('Y_obs', mu=mu, sd=sigma, observed=Y)
并提到这具有将变量 alpha
、beta
、sigma
、mu
和 Y_obs
关联到模型 basic_model.
我想了解这种机制是如何运作的。在我发现的 explanations of 上下文管理器中,我没有看到任何暗示上下文块中定义的变量或对象如何以某种方式 "associated" 进入上下文管理器的提示。库 (PyMC3) 似乎可以以某种方式访问 "current" 上下文管理器,因此它可以在幕后将每个新创建的语句关联到它。但是库如何访问上下文管理器?
我不知道在这个特定情况下它是如何工作的,但通常你会使用一些 'behind the scenes magic':
class Parent:
def __init__(self):
self.active_child = None
def ContextManager(self):
return Child(self)
def Attribute(self):
return self.active_child.Attribute()
class Child:
def __init__(self,parent):
self.parent = parent
def __enter__(self):
self.parent.active_child = self
def __exit__(self, exc_type, exc_val, exc_tb):
self.parent.active_child = None
def Attribute(self):
print("Called Attribute of child")
使用此代码:
p = Parent()
with p.ContextManager():
attr = p.Attribute()
将产生以下输出:
Called Attribute of child
PyMC3 通过在 Context
class 中维护 thread local variable 作为 class 变量 来做到这一点。 Model
s 继承自 Context
.
每次您在模型上调用 with
时,当前模型都会被推送到特定于线程的上下文堆栈中。因此,堆栈的顶部总是指用作上下文管理器的最里面(最近)的模型。
Context
s(因此 Model
s)有一个 .get_context()
class 方法 来获取顶部上下文堆栈。
Distribution
在创建它们时调用 Model.get_context()
以将它们自己与最内层模型相关联。
简而言之:
with model
将model
压入上下文堆栈。这意味着在with
块内,type(model).contexts
或Model.contexts
或Context.contexts
现在包含model
作为其最后(最顶层)元素。Distribution.__init__()
调用Model.get_context()
(注意大写M
),其中 returns 上下文堆栈的顶部。在我们的例子中,这是model
。上下文堆栈是线程本地的(每个线程有一个),但它不是特定于实例的。如果只有一个线程,那么也只有一个上下文堆栈,与模型数量无关。- 退出上下文管理器时。
model
从上下文堆栈中弹出。