据我了解,上下文管理器在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上下文管理器中,我没有看到任何建议暗示如何在上下文块中定义的变量或对象以某种方式“关联”到上下文管理器。似乎该库(PyMC3)以某种方式可以访问“当前”上下文管理器,因此它可以在幕后将每个新创建的语句与其关联。但是图书馆如何获得对上下文管理器的访问权限?
答案 0 :(得分:2)
在这种特定情况下,我不知道它是如何工作的,但通常您会使用一些“幕后魔术”:
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
答案 1 :(得分:1)
PyMC3通过在Context
类内将thread local variable保留为 class变量来实现。 Model
继承自Context
。
每次在模型上调用with
时,当前模型都会被推送到特定于线程的上下文堆栈中。因此,堆栈的顶部始终引用用作上下文管理器的最内部(最新)的模型。
Context
s(因此还有Model
s)具有.get_context()
类方法,以获取上下文堆栈的顶部。
Distribution
在创建Model.get_context()
并将其与最内部的模型相关联时会调用它们。
简而言之:
with model
将model
推送到上下文堆栈。这意味着在with
块中,type(model).contexts
或Model.contexts
或Context.contexts
现在包含model
作为其最后一个(最顶部)元素。Distribution.__init__()
调用Model.get_context()
(注意大写字母M
),该返回上下文堆栈的顶部。在我们的情况下,这是model
。上下文堆栈是线程本地的(每个线程一个),但是它不是特定于实例的。如果只有一个线程,那么无论模型数量如何,也只有一个上下文堆栈。model
从上下文堆栈中弹出。