Как создать динамические ограниченные по объему переменные в Python?

Я перевожу некоторый код от шепелявости до Python.

В шепелявости у Вас может быть конструкция, которой позволяют, с переменными, представленными объявленный как особенная и таким образом имеющий динамический контекст. (См. http://en.wikipedia.org/wiki/Dynamic_scope#Dynamic_scoping),

Как я могу сделать аналогично в Python? Кажется, что язык не поддерживает это непосредственно, если это правда, каков был бы хороший способ эмулировать его?

12
задан Dietrich Epp 29 January 2017 в 22:03
поделиться

3 ответа

Мне кажется, что Справедливость прямо в его рассуждениях.

С другой стороны -- я не могу устоять перед реализацией доказательства концепции для еще одной "неестественной" для 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()
11
ответ дан 2 December 2019 в 07:02
поделиться

Вот кое-что, что работает немного похоже на специальные переменные 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.

.
11
ответ дан 2 December 2019 в 07:02
поделиться

Динамический обзор, считающийся вредным.

Не используйте его, не эмулируйте.

Если необходимо эмулировать его, определите модуль dynamic_scope для эмуляции такого поведения и импортируйте модуль во все исходные файлы. В этом модуле должны быть методы begin, которые вызываются в первой строке ваших функций, использующих динамические диапазоны, end, get, и set. Методы get и set должны реализовать поиск цепочки вызовов имен переменных, где цепочка вызовов реализована с помощью begin и end. Затем рефакторизуйте свой код для устранения динамических областей применения.

.
-6
ответ дан 2 December 2019 в 07:02
поделиться
Другие вопросы по тегам:

Похожие вопросы: