如何在 Python 中创建动态范围的变量? [英] How to create dynamical scoped variables in Python?

查看:30
本文介绍了如何在 Python 中创建动态范围的变量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am translating some code from lisp to Python.

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

In lisp, you can have a let construct with the variables introduced declared as special and thus having dynamic scope. (See http://en.wikipedia.org/wiki/Dynamic_scope#Dynamic_scoping)

我如何在 Python 中做同样的事情?似乎语言不直接支持这个,如果是真的,有什么好方法可以模拟它?

How can I do likewise in Python? It seems the language does not support this directly, if true, what would be a good way to emulate it?

推荐答案

我觉得正义在这里的推理很简单.

I feel Justice is plain right in his reasoning here.

另一方面——我无法抗拒为 Python 的另一个不自然"的编程范式实现概念证明——我只是喜欢这样做.:-)

On the other hand -- I can't resist implementing proof of concept for still another programing paradigm "unnatural" to Python -- I simply love doing this. :-)

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

So, I created a class whose objects'attributes are scopped just like you require (and can be created dynamically). As I said, it is just in a proof of concept state - but I think most usual errors, (like trying to access a variable ina scope it is not defined at all) should have errors raised, even if not the proper ones (IndexError due to a stack underflow instead of AttributeError, for example)

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)

2020 update - Another similar question showed up, and I crafted a hack that needs no special namespace objects (but which resorts to using inner things from cPython, like updating the locals() to actual variables: https://stackoverflow.com/a/61015579/108205 (works with Python 3.8)

这篇关于如何在 Python 中创建动态范围的变量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆