Ordenado Comprensiones de Dict

¿Puedo extender la syntax en python para la comprensión de dictados de otros dictados, como el módulo OrderedDict en collections o mis propios tipos que heredan de dict ?

El simple hecho de volver a encuadrar el nombre del dictado no funciona, la syntax de comprensión {key: value} aún le da un dictado simple para las comprensiones y los literales.

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

Entonces, si es posible, ¿cómo podría hacer eso? Está bien si solo funciona en CPython. Para la syntax, supongo que lo intentaría con un prefijo O{k: v} como el que tenemos en los r'various' u'string' b'objects' .

Nota: por supuesto, podemos usar una expresión generadora, pero estoy más interesado en ver qué tan pirateable es Python en términos de gramática.

No hay una forma directa de cambiar la syntax de Python desde el idioma. Una comprensión de diccionario (o visualización simple) siempre creará un dict , y no hay nada que puedas hacer al respecto. Si está utilizando CPython, está utilizando códigos de byte especiales que generan un dictado directamente, que en última instancia llaman a las PyDict API de PyDict y / o a las mismas funciones subyacentes utilizadas por esa API. Si está utilizando PyPy, esos códigos de byte se implementan en su lugar sobre un objeto RPython dict que a su vez se implementa sobre un dict comstackdo y optimizado de Python. Y así.

Hay una forma indirecta de hacerlo, pero no te va a gustar. Si lees los documentos en el sistema de importación , verás que es el importador el que busca el código comstackdo en caché o llama al comstackdor, y el comstackdor que llama al analizador, y así sucesivamente. En Python 3.3 y versiones posteriores, casi todo en esta cadena está escrito en Python puro, o tiene una implementación Python pura alternativa, lo que significa que puede dividir el código y hacer lo suyo. Lo que incluye analizar la fuente con su propio código de PyParsing que genera AST, o comstackr un nodo AST de comprensión de dictado en su propio código de bytes personalizado en lugar del predeterminado, o el procesamiento posterior del código de bytes, o …

En muchos casos, un gancho de importación es suficiente; Si no, siempre puedes escribir un buscador y un cargador personalizados.

Si aún no está utilizando Python 3.3 o posterior, le sugeriría migrar antes de jugar con estas cosas. En versiones anteriores, es más difícil y está menos documentado, y en última instancia, se esforzará 10 veces más por aprender algo que será obsoleto cuando migre.

De todos modos, si este enfoque le parece interesante, es posible que desee echar un vistazo a MacroPy . Podría pedirle prestado algo de código y, lo que es más importante, aprender cómo se utilizan algunas de estas funciones (que no tienen buenos ejemplos en los documentos).

O, si estás dispuesto a conformarte con algo menos genial, puedes usar MacroPy para construir una “macro de comprensión odic” y usarla. (Tenga en cuenta que MacroPy actualmente solo funciona en Python 2.7, no 3.x.) No puede obtener o{…} , pero puede obtener, por ejemplo, od[{…}] , que no está tan mal. Descargue od.py , realmain.py y main.py , y ejecute python main.py para ver cómo funciona. La clave es este código, que toma un AST DictionaryComp , lo convierte en un GeneratorExpr equivalente en el valor clave de Tuple s, y lo envuelve en una Call a collections.OrderedDict .

 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 

Una alternativa diferente es, por supuesto, modificar el intérprete de Python.

Yo sugeriría descartar la idea de syntax O{…} para su primera vez, y simplemente hacer que las comprensiones normales de dictado se compilen en odictos. La buena noticia es que, en realidad, no es necesario cambiar la gramática (que está más allá de la peluda …), cualquiera de los siguientes:

  • los bytecodes que los compcomps comstackn,
  • la forma en que el intérprete ejecuta esos bytecodes, o
  • La implementación del tipo PyDict

La mala noticia, si bien todas ellas son mucho más fáciles que cambiar la gramática, ninguna de ellas puede hacerse desde un módulo de extensión. (Bueno, puedes hacer el primero haciendo básicamente lo mismo que harías desde Python puro … y puedes hacer cualquiera de ellos conectando el .so / .dll / .dylib al parche en tus propias funciones, pero eso es exactamente el mismo trabajo que piratear en Python más el trabajo adicional de enganchar en tiempo de ejecución.)

Si desea piratear la fuente CPython , el código que desea está en Python/compile.c , Python/ceval.c y Objects/dictobject.c , y la guía dev le indica cómo encontrar todo lo que necesita. Pero es posible que desee considerar piratear la fuente de PyPy en su lugar, ya que está escrito principalmente en (un subconjunto de) Python en lugar de C.


Como nota al margen, su bash no hubiera funcionado, incluso si todo se hubiera hecho a nivel de lenguaje Python. olddict, dict = dict, OrderedDict crea un enlace llamado dict en los módulos globales de su módulo, que oculta el nombre en los elementos incorporados, pero no lo reemplaza. Puedes reemplazar las cosas en las incorporaciones (bueno, Python no garantiza esto, pero hay implementaciones / versiones específicas que suceden en el trabajo para cada implementación / versión que he intentado …), pero lo que hiciste fue No es la forma de hacerlo.

Lo siento no es posible. Los literales de dictado y la comprensión de dictados se asignan al tipo de dictado incorporado, de una manera que está codificada en el nivel C. Eso no puede ser anulado.

Puedes usar esto como una alternativa, sin embargo:

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

Addendum: a partir de Python 3.6, todos los diccionarios de Python están ordenados. A partir de 3.7 , es incluso parte de la especificación de idioma. Si está usando esas versiones de Python, no es necesario que use OrderedDict: la comprensión del dictado solo funcionará (TM).

Modificando ligeramente la respuesta de @Max Noel, puede usar la comprensión de lista en lugar de un generador para crear un OrderedDict de forma ordenada (lo que, por supuesto, no es posible con la comprensión de dict).

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