Python __setattr__ и __getattr__ для глобальной области видимости? - PullRequest
4 голосов
/ 17 апреля 2010

Предположим, мне нужно создать свой собственный маленький DSL, который бы использовал Python для описания определенной структуры данных. Например. Я хотел бы иметь возможность написать что-то вроде

f(x) = some_stuff(a,b,c)

и пусть Python вместо того, чтобы жаловаться на необъявленные идентификаторы или пытаться вызвать функцию some_stuff, преобразует его в буквальное выражение для моего дальнейшего удобства.

Можно получить разумное приближение к этому, создав класс с правильно переопределенными методами __getattr__ и __setattr__ и использовать его следующим образом:

e = Expression()
e.f[e.x] = e.some_stuff(e.a, e.b, e.c)

Было бы здорово, если бы можно было избавиться от надоедливого «е». префиксы и, возможно, даже избегать использования []. Так что мне было интересно, возможно ли как-то временно «переопределить» глобальный поиск и присвоение имен? В связи с этим, может быть, есть хорошие пакеты для простого достижения такой «цитирующей» функциональности для выражений Python?

Ответы [ 3 ]

3 голосов
/ 17 апреля 2010

Я не уверен, что это хорошая идея, но я думал, что попробую . Подведем итог:

class PermissiveDict(dict):
    default = None

    def __getitem__(self, item):
        try:
            return dict.__getitem__(self, item)
        except KeyError:
            return self.default

def exec_with_default(code, default=None):
    ns = PermissiveDict()
    ns.default = default
    exec code in ns
    return ns
2 голосов
/ 17 апреля 2010

Возможно, вы захотите взглянуть на модули ast или parser, включенные в Python, для анализа, доступа и преобразования абстрактного синтаксического дерева (или дерева синтаксического анализа, соответственно) входного кода. Насколько я знаю, математическая система Sage , написанная на Python, имеет подобный вид прекомпилятора.

0 голосов
/ 18 мая 2010

В ответ на комментарий Вая вот одно забавное решение, которое я нашел. Прежде всего, чтобы еще раз объяснить, что он делает, предположим, что у вас есть следующий код:

definitions = Structure()
definitions.add_definition('f[x]', 'x*2')
definitions.add_definition('f[z]', 'some_function(z)')
definitions.add_definition('g.i', 'some_object[i].method(param=value)')

, где добавление определений подразумевает разбор левой и правой сторон и выполнение других неприятных вещей. Теперь один (не обязательно хороший, но, безусловно, веселый) подход позволил бы написать приведенный выше код следующим образом:

@my_dsl
def definitions():
    f[x] = x*2
    f[z] = some_function(z)
    g.i  = some_object[i].method(param=value)

и пусть Python сделает большую часть разбора под капотом. Идея основана на простом утверждении exec <code> in <environment>, упомянутом Яном, с одним хакерским дополнением. А именно, байт-код функции должен быть слегка изменен, и все локальные операции доступа к переменным (LOAD_FAST) переключаются на доступ к переменным из среды (LOAD_NAME).

Проще показать, чем объяснить: http://fouryears.eu/wp-content/uploads/pydsl/

Существуют различные приемы, которые вы можете сделать, чтобы сделать это практичным. Например, в коде, представленном по ссылке выше, вы не можете использовать встроенные функции и языковые конструкции, такие как for, и операторы if внутри функции @my_dsl. Однако вы можете заставить их работать, добавив больше поведения в класс Env.

Обновление . Здесь - более подробное объяснение того же самого.

...