Я перевожу некоторый код от шепелявости до Python.
В шепелявости у Вас может быть конструкция, которой позволяют, с переменными, представленными объявленный как особенная и таким образом имеющий динамический контекст. (См. http://en.wikipedia.org/wiki/Dynamic_scope#Dynamic_scoping),
Как я могу сделать аналогично в Python? Кажется, что язык не поддерживает это непосредственно, если это правда, каков был бы хороший способ эмулировать его?
Мне кажется, что Справедливость прямо в его рассуждениях.
С другой стороны -- я не могу устоять перед реализацией доказательства концепции для еще одной "неестественной" для Python парадигмы программирования -- мне просто нравится это делать. :-)
Итак, я создал класс, атрибуты объектов которого оцениваются так, как вы требуете (и могут быть созданы динамически). Как я уже говорил, это просто доказательство концептуального состояния - но я думаю, что большинство обычных ошибок (например, попытка доступа к переменной в области видимости, которая не определена вообще) должны иметь повышенный уровень ошибок, даже если они не являются правильными (например, IndexError из-за underflow стека вместо 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()
Вот кое-что, что работает немного похоже на специальные переменные Lisp, но немного лучше подходит для 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()
Вы можете использовать его следующим образом:
with env.let(bufsize=8192, encoding="ascii"):
print env.bufsize # prints 8192
a() # call a function that uses env.bufsize or env.encoding
Эффекты env.let
продолжаются в течение с блоком
.
Обратите внимание, что если вы используете потоки, то вам определенно понадобится разный _stack
для каждого потока. Для реализации этого можно использовать threading.local.
Динамический обзор, считающийся вредным.
Не используйте его, не эмулируйте.
Если необходимо эмулировать его, определите модуль dynamic_scope
для эмуляции такого поведения и импортируйте модуль во все исходные файлы. В этом модуле должны быть методы begin
, которые вызываются в первой строке ваших функций, использующих динамические диапазоны, end
, get
, и set
. Методы get
и set
должны реализовать поиск цепочки вызовов имен переменных, где цепочка вызовов реализована с помощью begin
и end
. Затем рефакторизуйте свой код для устранения динамических областей применения.