Aquí hay algo que funciona un poco como variables especiales de Lisp, pero se ajusta un poco mejor en Python.
_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()
Se puede utilizar la siguiente manera:
with env.let(bufsize=8192, encoding="ascii"):
print env.bufsize # prints 8192
a() # call a function that uses env.bufsize or env.encoding
Los efectos de env.let
últimos durante la duración del bloque with
.
Tenga en cuenta que si utiliza subprocesos, definitivamente querrá un _stack
diferente para cada subproceso. Puede usar threading.local para implementar eso.
¡Felicitaciones! Gracias a su arduo trabajo, el mundo de la programación tiene una solución más que se abrirá paso en el corazón de numerosas aplicaciones críticas. – yfeldblum
Y, después de todo, las "variables especiales" de Lisp no son tan terribles, ¿verdad? Son como variables de entorno en bash. Lo que es terrible son los idiomas en los que el alcance dinámico es el predeterminado. Afortunadamente, no quedan muchos. –
Me alegra que la mayoría de los lenguajes reales no usen el alcance dinámico ... y, sin embargo, he escrito un montón de Emacs Lisp, que sí; y se siente completamente natural para mí. (Emacs Lisp obtuvo recientemente el alcance léxico como una opción, y nunca me he molestado en usarlo :-) – offby1