OrderedDict理解

时间:2014-01-13 23:57:53

标签: python dictionary cpython ordereddictionary dictionary-comprehension

我可以扩展python中的语法,用于其他dicts的dict理解,比如collections模块中的OrderedDict或继承自dict的我自己的类型吗?

重新绑定dict名称显然不起作用,{key: value}理解语法仍然为您提供了一个简单的旧词典,用于理解和文字。

>>> from collections import OrderedDict
>>> olddict, dict = dict, OrderedDict
>>> {i: i*i for i in range(3)}.__class__
<type 'dict'>

那么,如果有可能我该怎么做呢?如果它只适用于CPython,那就没关系。对于语法,我想我会尝试使用O{k: v}前缀,就像我们在r'various' u'string' b'objects'上一样。

注意:当然我们可以使用生成器表达式,但是我更感兴趣的是看看python在语法方面是多么糟糕。

3 个答案:

答案 0 :(得分:83)

抱歉,不可能。 Dict文字和字典理解以一种在C级硬编码的方式映射到内置字典类型。这不能被覆盖。

您可以使用此替代方法:

OrderedDict((i, i * i) for i in range(3))

附录:从Python 3.6开始,所有Python词典都是有序的。 As of 3.7,它甚至是语言规范的一部分。如果您正在使用这些版本的Python,则不需要OrderedDict:dict理解将为Just Work(TM)。

答案 1 :(得分:29)

没有直接的方法可以在语言中更改Python的语法。字典理解(或简单显示)总是会创建一个dict,而你无能为力。如果您正在使用CPython,它会使用直接生成dict的特殊字节码,最终调用PyDict API函数和/或该API使用的相同底层函数。如果您正在使用PyPy,那么这些字节码将在RPython dict对象之上实现,而该对象又在已编译和优化的Python dict之上实现。等等。

有一种间接方式,但你不会喜欢它。如果您阅读the import system上的文档,您会发现它是搜索缓存编译代码或调用编译器的导入程序,以及调用解析器的编译器,依此类推。在Python 3.3+中,这个链中的几乎所有东西都是用纯Python编写的,或者有一个替代的纯Python实现,这意味着你可以分叉代码并做自己的事情。其中包括使用您自己构建AST的PyParsing代码解析源代码,或者将dict理解AST节点编译为您自己的自定义字节码而不是默认字节码,或者后处理字节码,或者......

在许多情况下,import hook就足够了;如果没有,你总是可以编写自定义查找程序和加载程序。

如果您还没有使用Python 3.3或更高版本,我强烈建议您在使用这些内容之前进行迁移。在较旧的版本中,它更难,而且文档记录较少,并且您最终会花费10倍的时间来学习在迁移时将会过时的内容。

无论如何,如果这种方法对您来说很有意思,您可能需要查看MacroPy。您可以从中借用一些代码 - 并且更重要的是,可以了解如何使用其中一些功能(在文档中没有很好的示例)。

或者,如果您愿意为不太酷的事情做出让步,您可以使用MacroPy构建一个&#34; odict理解宏&#34;并使用它。 (请注意,MacroPy目前仅适用于Python 2.7,而不是3.x.)您无法获得o{…},但您可以获得od[{…}],这不是{\ n}太糟糕了。下载od.pyrealmain.pymain.py,然后运行python main.py以查看其是否有效。关键是这段代码,它采用DictionaryComp AST,将其转换为键值GeneratorExpr上的等效Tuple,并将其包装在Call到{{ 1}}:

collections.OrderedDict

当然,另一种选择是修改Python解释器。

我建议你先放弃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 语法的想法,然后将正常的dict理解编译成odicts。好消息是,你真的不需要改变语法(超出毛茸茸......),只需要以下任何一种语法:

  • dictcomps编译到的字节码,
  • 解释器运行这些字节码的方式,或
  • O{…}类型
  • 的实施

坏消息虽然所有这些都比改变语法容易得多,但是没有一个可以通过扩展模块完成。 (嗯,你可以做第一个基本上与纯Python相同的事情......你可以通过挂钩.so / .dll / .dylib来修补你自己的函数,但这与在黑客上攻击Python以及在运行时挂钩的额外工作完全相同。)

如果你想攻击CPython source,你想要的代码在PyDictPython/compile.cPython/ceval.cdev guide告诉你如何找到你需要的一切。但是你可能想考虑在PyPy source上进行黑客攻击,因为它主要是用Python(而不是C)的一部分编写的。


作为旁注,即使所有内容都是在Python语言级别完成,您的尝试也无法奏效。 Objects/dictobject.c在您的模块的全局变量中创建一个名为olddict, dict = dict, OrderedDict的绑定,其中阴影内置的名称,但不替换它。你可以替换内置的东西(嗯,Python并不能保证这一点,但是有一些实现/特定于版本的东西 - 对于每个实现/版本而言都是如此 - &#39我试过......),但你所做的并不是这样做的。

答案 2 :(得分:14)

略微修改@Max Noel的响应,您可以使用列表推导而不是生成器以有序的方式创建OrderedDict(使用dict理解当然不可能)。

>>> OrderedDict([(i, i * i) for i in range(5)])
OrderedDict([(0, 0), 
             (1, 1), 
             (2, 4), 
             (3, 9), 
             (4, 16)])