python - 如何在 Python 中创建动态范围变量?

标签 python variables lisp scope dynamic-scope

我正在将一些代码从 lisp 翻译成 Python。

在 lisp 中,您可以使用 let 构造,其中引入的变量声明为特殊变量,因此具有动态范围。 (参见 http://en.wikipedia.org/wiki/Dynamic_scope#Dynamic_scoping)

我怎样才能在 Python 中做同样的事情?似乎该语言不直接支持这一点,如果是这样,那么模拟它的好方法是什么?

最佳答案

我觉得 Justice 在这里的推理是显而易见的。

另一方面——我无法抗拒为另一个对 Python 来说“不自然”的编程范例实现概念验证——我只是喜欢这样做。 :-)

因此,我创建了一个类,其对象的属性按照您的需要进行限定(并且可以动态创建)。正如我所说,它只是处于概念验证状态——但我认为大多数常见的错误,(比如试图访问一个根本没有定义的范围内的变量)应该引发错误,即使不是正确的错误(IndexError例如,由于堆栈下溢而不是 AttributeError)

import inspect


class DynamicVars(object):
    def __init__(self):
        object.__setattr__(self, "variables", {})

    def normalize(self, stackframe):
        return [hash(tpl[0]) for tpl in stackframe[1:]]

    def __setattr__(self, attr, value):
        stack = self.normalize(inspect.stack())
        d = {"value": value, "stack": stack}
        if not attr in self.variables:
            self.variables[attr] = []
            self.variables[attr].append(d)
        else:
            our_value = self.variables[attr]
            if our_value[-1]["stack"] == stack:
                our_value[-1]["value"] = value
            elif len(stack) <= len(our_value):
                while our_value and stack !=  our_value["stack"]:
                    our_value.pop()
                our_value.append(d)
            else: #len(stack) > len(our_value):
                our_value.append(d)
    def __getattr__(self, attr):
        if not attr in self.variables:
            raise AttributeError
        stack = self.normalize(inspect.stack())
        while self.variables[attr]:
            our_stack = self.variables[attr][-1]["stack"]
            if our_stack == stack[-len(our_stack):]:
                break
            self.variables[attr].pop()
        else:
            raise AttributeError
        return self.variables[attr][-1]["value"]


# for testing:
def c():
    D = DynamicVars()
    D.c = "old"
    print D.c
    def a():
        print D.c
    a()
    def b():
        D.c = "new"
        a()
    b()
    a()
    def c():
        D.c = "newest"
        a()
        b()
        a()
    c()
    a()

c()

2020 更新 - 出现了另一个类似的问题,我设计了一个不需要特殊命名空间对象的 hack(但它求助于使用 cPython 的内部事物,比如将 locals() 更新为实际变量: https://stackoverflow.com/a/61015579/108205(适用于 Python 3.8)

关于python - 如何在 Python 中创建动态范围变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2001138/

相关文章:

lisp - 自托管的概念

Emacs Lisp 分割标识和删除的唯一窗口名称

python - 我可以同时调用和设置库中的 Python gettext 模块和使用它的模块吗?

python - 如果以 `ps -ef` 启动,`subprocess.Popen` 显示运行进程两次

Delphi访问冲突分配局部变量

c - 为什么用户输入的变量结果是 0 - C?

printing - write、print、pprint、princ 和 prin1 之间有什么区别?

python - 在python opencv中为像素着色

python - 如何通过Python打开不同类型的网络浏览器?

java - 这不是用 Java 解释 Final 的一个坏例子吗?