2010-04-17 6 views
4

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

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?

+0

"раздражающий" e. "Префиксы"? Они кажутся абсолютно необходимыми для понимания того, что происходит. Если 'some_stuff' не является функцией, а является секретным методом анонимного объекта, я не понимаю, как кто-то может научиться его использовать. –

+0

Ну, научиться использовать это является не связанным с этим вопросом соответствующей документации.Обратите внимание, что в моем примере some_stuff __not__ ссылается также на существующую функцию объекта «выражение» - он будет «создан динамически» с помощью '__getattr__'. То, что я в основном ищу, - это способ создания сексуального синтаксического сахара, который может заменить такие вещи, как, например, 'add_new_formula (['f', 'x'], ['some_stuff', 'a', 'b' 'c']) 'чем-то более читаемым. В одном из ответов упоминается Sage, посмотрите, как он использует измененный синтаксис Python для живого примера. –

+1

@KT: Ваши различные примеры по всей карте. Один предполагает слишком много, у одного может быть слишком много ненужных ссылок на объекты на один и тот же объект, а у другого слишком много цитат и скобок. Есть ли что-нибудь, что вы можете предоставить, чтобы показать рабочий пример того, что вы пытаетесь сделать со стандартными классами объектов? Не могли бы вы опустить гиперболу? Не могли бы вы предоставить рабочие классы, чтобы мы могли упростить синтаксис? –

ответ

3

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

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 
+1

Удивительный! «Exec ... in ...» - это более или менее то, что я искал. Теперь я могу помещать этот материал в аннотацию и иметь некоторую магию, происходящую внутри данной функции. Вероятно, невозможно сделать его более синтаксическим - сладким, чем это. Благодаря! –

+0

Хех, к сожалению, это решение работает по-разному в зависимости от того, откуда вы получаете объект кода. Если это строка или результат компиляции(), все работает так, как ожидалось. Однако, если объект кода является чем-то вроде my_function.func_code, это не удается из-за особенностей привязки переменных внутри функции - локальные переменные не будут отображаться в предоставленной среде. Это может быть преодолено путем бад-кода mangling (см. Код_all_variables_dynamic здесь: http://code.activestate.com/recipes/498242/). Это делает все более сложным, чем могло бы быть. –

+0

Да ... эти проблемы никогда не прекратятся, поэтому было бы лучше работать в рамках того, что доступно без хаков, если это вообще возможно. –

2

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

+0

Спасибо, это одна возможность. Тем не менее, это не совсем то, что мне хотелось бы, так как потребовалось бы, чтобы я отделял свои «выражения, которые нужно разбирать» от обычного питона, передавая их в виде строк, что было бы не так страшно, если бы можно было продолжать использовать стандартный анализ и просто переопределить поведение оценки ('__getattr__' и т. п.). –

-1

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

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/

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

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