python 上下文与变量作用域有何关系?

How do python contexts relate to variable scoping?

我阅读了“”Bayesian Methods for Hackers”中的示例,我注意到一种 "magic" 是通过 python 上下文获得的:

In PyMC3, we typically handle all the variables we want in our model within the context of the Model object. This is an extra layer of convenience compared to PyMC. Any variables created within a given Model's context will be automatically assigned to that model. If you try to define a variable outside of the context of a model, yo u will get an error.

有问题的代码是这个

import pymc3 as pm

with pm.Model() as model:
    parameter = pm.Exponential("poisson_param", 1.0)
    data_generator = pm.Poisson("data_generator", parameter)

我试图用自定义玩具 class 复制此行为,该玩具定义了 __enter____exit__ 方法,但无法创建 class 的工作原理 "Any variables created within a given Model's context will be automatically assigned to that model"

当然可以。

对象基本上是 "smart",因为它们以某种方式知道当前上下文(模型)是什么并在其中注册自己。

这是一个示例(它也支持输入嵌套上下文,尽管它们之间目前没有任何类型的 parent/child 层次结构)。

class Context:
    # Stack of current contexts entered; stored as a class attribute
    stack = []

    def __init__(self, name):
        self.name = name
        self.vars = {}

    def __enter__(self):
        # Put self on the stack.
        Context.stack.append(self)
        return self

    def __exit__(self, et, ev, tb):
        # Ensure we're popping ourselves off the stack.
        assert Context.stack[-1] is self
        # Remove self.
        Context.stack.pop(-1)

    def register(self, var):
        # Register a `Var` in self...
        self.vars[var.name] = var
        # ... and set its Context.
        var.context = self

    @staticmethod
    def get_current():
        # Get the topmost Context if any.
        return Context.stack[-1] if Context.stack else None

    def __repr__(self):
        return "<Context %r with %d vars>" % (self.name, len(self.vars))


class Var:
    def __init__(self, name):
        self.context = None
        self.name = name

        # Register ourselves in the current context, if any.
        ctx = Context.get_current()
        if ctx:
            ctx.register(self)

    def __repr__(self):
        return "<%s (in %s)>" % (self.name, self.context)


with Context("spam") as c:
    v = Var("foo")
    x = Var("bar")

print(c.vars)