Python中的__setattr__和__getattr__用于全局范围?

6 投票
3 回答
2226 浏览
提问于 2025-04-15 21:42

假设我想创建一个自己的小语言,用Python来描述某种数据结构。比如,我希望能够写出类似下面的代码:

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

然后希望Python能够理解这些,而不是因为找不到某些变量或者试图调用某个函数而报错,而是把它转换成我能用的表达方式。

其实可以通过创建一个类,并重新定义一些特殊的方法,比如__getattr____setattr__,来达到一个比较接近的效果,使用方法如下:

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

不过,如果能去掉那些烦人的“e.”前缀,甚至不需要用到方括号,那就更好了。所以我在想,是否有办法暂时“重新定义”全局变量的查找和赋值呢?另外,是否有一些好的库可以轻松实现这种“引用”功能,让Python表达式更方便呢?

3 个回答

-1

针对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就可以在后台处理大部分解析工作。这个想法基于Ian提到的简单的 exec <code> in <environment> 语句,并加了一点小技巧。具体来说,函数的字节码需要稍微调整,把所有本地变量的访问操作(LOAD_FAST)改为从环境中访问变量(LOAD_NAME)。

这个过程用展示的方式比用解释的方式更容易理解:http://fouryears.eu/wp-content/uploads/pydsl/

你可能想要做一些不同的小技巧来让它更实用。例如,在上面链接中的代码里,你不能在 @my_dsl 函数内使用内置函数和语言结构,比如for循环和if语句。不过,你可以通过给Env类添加更多功能来让这些也能工作。

更新这里有一个稍微详细一点的解释,讲的也是同样的内容。

2

你可以看看Python自带的astparser模块,这些模块可以用来解析、访问和转换输入代码的抽象语法树(或者叫解析树)。据我所知,使用Python编写的Sage数学系统也有类似的预编译功能。

3

我不太确定这样做是否合适,但我想试试看。下面是总结:

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

撰写回答