Python __setattr__ 和 __getattr__ 用于全局范围?

发布于 2024-08-29 08:56:26 字数 495 浏览 5 评论 0原文

假设我需要创建自己的小型 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)

不过,如果它是这样的话,那就太酷了可以摆脱烦人的“e”。前缀,甚至可能避免使用 []。所以我想知道,是否有可能以某种方式暂时“重新定义”全局名称查找和分配?与此相关的是,也许有好的软件包可以轻松实现 Python 表达式的这种“引用”功能?

Suppose I need to create my own small DSL that would use Python to describe a certain data structure. E.g. I'd like to be able to write something like

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

and have Python, instead of complaining about undeclared identifiers or attempting to invoke the function some_stuff, convert it to a literal expression for my further convenience.

It is possible to get a reasonable approximation to this by creating a class with properly redefined __getattr__ and __setattr__ methods and use it as follows:

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

It would be cool though, if it were possible to get rid of the annoying "e." prefixes and maybe even avoid the use of []. So I was wondering, is it possible to somehow temporarily "redefine" global name lookups and assignments? On a related note, maybe there are good packages for easily achieving such "quoting" functionality for Python expressions?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

九局 2024-09-05 08:56:26

我不确定这是一个好主意,但我想我应该给它尝试一下。总结一下:

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

I'm not sure it's a good idea, but I thought I'd give it a try. To summarize:

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
时光是把杀猪刀 2024-09-05 08:56:26

您可能想看看 Python 中包含的 astparser 模块,以解析、访问和转换输入的抽象语法树(或分别为解析树)代码。据我所知,用 Python 编写的 Sage 数学系统也有类似的预编译器。

You might want to take a look at the ast or parser modules included with Python to parse, access and transform the abstract syntax tree (or parse tree, respectively) of the input code. As far as I know, the Sage mathematical system, written in Python, has a similar sort of precompiler.

放我走吧 2024-09-05 08:56:26

为了回应 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)')

其中添加定义意味着解析左侧和右侧以及做其他丑陋的事情。现在,这里的一种(不一定好,但肯定有趣)方法允许将上面的代码编写如下:

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

并让 Python 在幕后完成大部分解析。
这个想法基于简单的 exec ;在 Ian 提到的 语句中,添加了一个黑客行为。也就是说,必须稍微调整函数的字节码,并将所有局部变量访问操作 (LOAD_FAST) 切换为来自环境的变量访问 (LOAD_NAME)。

显示比解释更容易: http://fouryears.eu/wp-content/uploads/ pydsl/

您可能需要使用各种技巧来使其实用。例如,在上面链接提供的代码中,您不能使用内置函数和语言结构,例如 @my_dsl 函数中的 for 循环和 if 语句。不过,您可以通过向 Env 类添加更多行为来实现这些功能。

更新这里是对同一件事的稍微更详细的解释。

In response to Wai's comment, here's one fun solution that I've found. First of all, to explain once more what it does, suppose that you have the following code:

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)')

where adding definitions implies parsing the left hand sides and the right hand sides and doing other ugly stuff. Now one (not necessarily good, but certainly fun) approach here would allow to write the above code as follows:

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

and have Python do most of the parsing under the hood.
The idea is based on the simple exec <code> in <environment> statement, mentioned by Ian, with one hackish addition. Namely, the bytecode of the function must be slightly tweaked and all local variable access operations (LOAD_FAST) switched to variable access from the environment (LOAD_NAME).

It is easier shown than explained: http://fouryears.eu/wp-content/uploads/pydsl/

There are various tricks you may want to do to make it practical. For example, in the code presented at the link above you can't use builtin functions and language constructions like for loops and if statements within a @my_dsl function. You can make those work, however, by adding more behaviour to the Env class.

Update. Here is a slightly more verbose explanation of the same thing.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文