我正在将一些代码从 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/