Python中的__setattr__和__getattr__用于全局范围?
假设我想创建一个自己的小语言,用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 个回答
针对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类添加更多功能来让这些也能工作。
更新。这里有一个稍微详细一点的解释,讲的也是同样的内容。
你可以看看Python自带的ast
或parser
模块,这些模块可以用来解析、访问和转换输入代码的抽象语法树(或者叫解析树)。据我所知,使用Python编写的Sage数学系统也有类似的预编译功能。
我不太确定这样做是否合适,但我想试试看。下面是总结:
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