没有直接的方法可以从语言内部更改Python的语法。无论是使用字典推导式(或普通显示),它总是会创建一个 dict
,你无法改变这个事实。如果你正在使用CPython,则使用特殊的字节码直接生成字典,最终调用PyDict
API函数和/或该API使用的底层函数。如果你正在使用PyPy,则这些字节码代替上面所述的 API 函数和对象,基于一个编译和优化的Python dict
对象和RPython dict
对象来实现。如此类推。
有一种间接的方法可以做到这一点,但你可能不会喜欢它。如果你阅读导入系统的文档,你会发现是导入器搜索缓存编译代码或调用编译器,编译器调用解析器等等。在Python 3.3+中,几乎所有这些链条中的东西都是用纯Python编写的,或者有一个替代的纯Python实现,这意味着你可以分叉代码并做自己的事情。这包括使用你自己的PyParsing代码构建AST来解析源代码,或将字典推导式AST节点编译成你自己的自定义字节码而不是默认值,或后处理字节码等等。
在许多情况下,导入钩子就足够了;如果不行,你可以编写自定义的查找器和加载器。
如果你还没有使用Python 3.3或更高版本,我强烈建议在尝试这些内容之前进行迁移。在旧版本中,这很难,并且文档也不是很好,你最终将花费10倍的精力学习某些东西,而无论何时你迁移,它都将过时。
无论如何,如果您对此方法感兴趣,您可能想看看MacroPy。您可以从中借鉴一些代码,更重要的是,了解如何使用一些在文档中没有好的示例的功能。
或者,如果你愿意接受不太酷的东西,你可以使用MacroPy
构建一个"odict comprehension macro"并使用它。(注意,MacroPy目前仅适用于Python 2.7,而不是3.x。) 你不能完全得到o{…}
,但你可以得到,比如说,od[{…}]
,这也不错。下载od.py
, realmain.py
, 和 main.py
,然后运行python main.py
查看它的工作原理。关键在于这段代码,它将DictionaryComp
AST转换为等效的基于键值Tuple
的GeneratorExpr
,并将其包装在对collections.OrderedDict
的Call
中:
def od(tree, **kw):
pair = ast.Tuple(elts=[tree.key, tree.value])
gx = ast.GeneratorExp(elt=pair, generators=tree.generators)
odict = ast.Attribute(value=ast.Name(id='collections'),
attr='OrderedDict')
call = ast.Call(func=odict, args=[gx], keywords=[])
return call
另一种选择是修改Python解释器。我建议您在首次尝试时放弃使用
O{...}
语法想法,而只需使普通字典推导编译为有序字典。好消息是,您不需要真正改变语法(这是超级复杂的...),只需要更改以下任意一项:
- 字典推导编译成的字节码
- 解释器运行这些字节码的方式
PyDict
类型的实现
坏消息是,虽然所有这些都比更改语法容易得多,但它们都不能从扩展模块中完成。(好吧,您可以通过执行基本上与纯Python相同的操作来执行第一个操作...并且您可以通过钩入.so/.dll/.dylib来补丁自己的函数来执行其中任何一个,但那就像是对Python进行黑客攻击加上额外的工作,即在运行时挂钩。)
如果您想要研究
CPython源码,那么您需要的代码在
Python/compile.c
、
Python/ceval.c
和
Objects/dictobject.c
中,而
dev guide会告诉您如何找到您需要的一切。但是,考虑到它主要是用Python(而不是C)编写的,您可能希望考虑研究
PyPy源码。
作为旁注,即使所有内容都在Python语言级别完成,您的尝试也不会成功。
olddict,dict = dict,OrderedDict
在模块的全局范围内创建了一个名为
dict
的绑定,这个绑定
遮蔽了内置的名称,但没有替换它。您
可以替换内置的东西(好吧,Python不能保证这一点,但是对于我尝试过的每个实现/版本,都有特定于实现/版本的事情-发生工作),但您所做的并不是这样做的方式。