from __future__ import with_statement # if Python 2.5
from contextlib import contextmanager
import threading
dyn = threading.local()
@contextmanager
def dyn_vars(**new):
old = {}
for name, value in new.items():
old[name] = getattr(dyn, name, None)
setattr(dyn, name, value)
yield
for name, value in old.items():
setattr(dyn, name, value)
_stack = []
class _EnvBlock(object):
def __init__(self, kwargs):
self.kwargs = kwargs
def __enter__(self):
_stack.append(self.kwargs)
def __exit__(self, t, v, tb):
_stack.pop()
class _Env(object):
def __getattr__(self, name):
for scope in reversed(_stack):
if name in scope:
return scope[name]
raise AttributeError("no such variable in environment")
def let(self, **kwargs):
return _EnvBlock(kwargs)
def __setattr__(self, name, value):
raise AttributeError("env variables can only be set using `with env.let()`")
env = _Env()
对应于Lisp“特殊”或动态范围变量的Python习惯用法是“线程本地存储”。在
这里有一个很好的讨论:What is "thread local storage" in Python, and why do I need it?
如果要完全模拟Lisp的特殊变量(包括let语句),可以使用上下文管理器:
示例(显然很傻,但它显示了可重入特性):
^{pr2}$我觉得他的推理是公正的。在
另一方面,我忍不住为另一个编程范式实现概念证明,这对Python来说是“不自然的”——我只是喜欢这样做。:-)
因此,我创建了一个类,它的对象属性的范围与您需要的一样(并且可以动态创建)。正如我所说,它只是处于概念验证状态-但我认为大多数常见的错误(如试图在根本没有定义的范围内访问变量)应该引发错误,即使不是正确的错误(例如,由于堆栈下溢而不是AttributeError导致的索引器错误)
这里有一些类似于Lisp的特殊变量的方法,但是它更适合Python。在
你可以这样使用它:
^{pr2}$env.let
的影响持续到with
块的持续时间。在请注意,如果您使用线程,您肯定需要为每个线程使用不同的
_stack
。您可以使用threading.local来实现它。在相关问题 更多 >
编程相关推荐