Python __Setattr__ и __getattr__ для глобальной массы?
-
27-09-2019 - |
Вопрос
Предположим, мне необходимо создать собственные маленькие DSL, которые будут использовать Python для описания определенной структуры данных. Например, я хотел бы иметь возможность написать что-то вроде
f(x) = some_stuff(a,b,c)
И иметь Python, вместо того, чтобы жаловаться на необъявленные идентификаторы или попытки вызвать функцию umy_Stuff, преобразуйте его в буквальное выражение для моего дополнительного удобства.
Для этого можно получить разумное приближение, создавая класс с должным образом переопределенным __getattr__
и __setattr__
Методы и используйте его следующим образом:
e = Expression()
e.f[e.x] = e.some_stuff(e.a, e.b, e.c)
Хотя было бы круто, если бы можно было избавиться от раздражающего «е». префиксы и, возможно, даже избежать использования []. Поэтому мне было интересно, возможно ли как-то временно «переопределить» глобальные поиски и заданиями? В соответствующем примечании, возможно, есть хорошие пакеты для легко достижения таких «цитирующих» функциональности для выражений Python?
Решение
Я не уверен, что это хорошая идея, но я думал, что попробовать. Отказ Подвести итоги:
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
Другие советы
Возможно, вы захотите взглянуть на ast
или parser
Модули включены в Python в анализ, доступ и преобразовать абстрактное синтаксическое дерево (или дерево разбора, соответственно) входного кода. Насколько я знаю, Мудрома математическая система, написанный в Python, имеет аналогичный сорт прекомпилеров.
В ответ на комментарий WAI вот одно забавное решение, которое я нашел. Прежде всего, объяснить еще раз, что он делает, предположим, что у вас есть следующий код:
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)')
Где добавление определений подразумевает расстановку левых сторон и правые стороны и выполняя другие уродливые вещи. Теперь один (не обязательно хороший, но, безусловно, Fun) подход здесь позволит написать вышеуказанный код следующим образом:
@my_dsl
def definitions():
f[x] = x*2
f[z] = some_function(z)
g.i = some_object[i].method(param=value)
И у Питона делают большую часть расставания под капотом. Идея основана на простом exec <code> in <environment>
Заявление, упомянутое IAN, с одним хакерским дополнением. А именно, Bytecode функции должен быть немного настраиваться, и все локальные операции доступных доступных доступа (Load_fast) перешли на переменную доступ от среды (Load_name).
Это легче показано, чем объяснено: http://fouryears.eu/wp-content/uploads/pydsl/
Есть различные трюки, которые вы можете сделать, чтобы сделать его практичным. Например, в указанном на ссылке выше, вы не можете использовать встроенные функции и языковые конструкции, такие как для циклов, и если выступления в функции @my_dsl. Однако вы можете сделать эти работы, добавив больше поведения в класс Env.
Обновлять. Здесь немного более глубоко объяснение того же.