3 Stimmen

Umkehrbare Version von compile() in Python

Ich versuche, eine Funktion in Python zu machen, die das Äquivalent von compile() tut, aber lässt mich auch die ursprüngliche Zeichenfolge zurück. Nennen wir die beiden Funktionen comp() und decomp(), um sie voneinander zu unterscheiden. Das heißt,

a = comp("2 * (3 + x)", "", "eval")
eval(a, dict(x=3)) # => 12
decomp(a) # => "2 * (3 + x)"

Die zurückgegebene Zeichenkette muss nicht identisch ("2*(3+x)" wäre akzeptabel), aber es muss im Grunde dasselbe sein ("2 * x + 6" wäre es nicht).

Ich habe Folgendes ausprobiert nicht Arbeit:

  • Setzen eines Attributs für das von compile zurückgegebene Codeobjekt. Sie können keine benutzerdefinierten Attribute für Codeobjekte festlegen.
  • Unterklassifizierung von Code, damit ich das Attribut hinzufügen kann. Code kann nicht unterklassifiziert werden.
  • Einrichtung eines WeakKeyDictionary, das Code-Objekte auf die ursprünglichen Zeichenketten abbildet. Code-Objekte können nicht schwach referenziert werden.

Hier ist, was funktioniert, mit Problemen:

  • Übergabe der ursprünglichen Codezeichenkette für den Dateinamen an compile(). Allerdings verliere ich die Fähigkeit, tatsächlich einen Dateinamen dort zu halten, die ich auch tun möchte.
  • Beibehaltung eines echten Wörterbuchs, das Codeobjekte auf Zeichenketten abbildet. Dies leckt Speicher, obwohl seit Kompilieren ist selten, es ist akzeptabel für meinen aktuellen Anwendungsfall. Ich könnte wahrscheinlich laufen die Schlüssel durch gc.get_referrers regelmäßig und töten aus toten, wenn ich musste.

6voto

Miles Punkte 29684

Das ist ein ziemlich merkwürdiges Problem, und meine erste Reaktion ist, dass Sie vielleicht besser etwas ganz anderes tun sollten, um das zu erreichen, was Sie vorhaben. Aber es ist immer noch eine interessante Frage, also hier ist mein Versuch: Ich mache die ursprüngliche Codequelle zu einer unbenutzten Konstante des Codeobjekts.

import types

def comp(source, *args, **kwargs):
    """Compile the source string; takes the same arguments as builtin compile().
    Modifies the resulting code object so that the original source can be
    recovered with decomp()."""
    c = compile(source, *args, **kwargs)
    return types.CodeType(c.co_argcount, c.co_nlocals, c.co_stacksize, 
        c.co_flags, c.co_code, c.co_consts + (source,), c.co_names, 
        c.co_varnames, c.co_filename, c.co_name, c.co_firstlineno, 
        c.co_lnotab, c.co_freevars, c.co_cellvars)

def decomp(code_object):
    return code_object.co_consts[-1]

>>> a = comp('2 * (3 + x)', '', 'eval')
>>> eval(a, dict(x=3))
12
>>> decomp(a)
'2 * (3 + x)'

4voto

Silverfish Punkte 1673

Mein Ansatz wäre, das Code-Objekt in ein anderes Objekt zu verpacken. Etwas wie dies:

class CodeObjectEnhanced(object):
    def __init__(self, *args):
        self.compiled = compile(*args)
        self.original = args[0]
def comp(*args):
    return CodeObjectEnhanced(*args)

Wenn Sie dann das eigentliche Code-Objekt benötigen, verwenden Sie a.compiled, und wenn Sie das Original benötigen, verwenden Sie a.original. Es könnte eine Möglichkeit geben, eval dazu zu bringen, die neue Klasse so zu behandeln, als wäre sie ein gewöhnliches Code-Objekt, indem man die Funktion umleitet und stattdessen eval(self.compiled) aufruft.

Ein Vorteil ist, dass die ursprüngliche Zeichenfolge gleichzeitig mit dem Codeobjekt gelöscht wird. Wie auch immer Sie dies tun, ich denke, die Speicherung der ursprünglichen Zeichenfolge ist wahrscheinlich der beste Ansatz, da Sie am Ende mit der genauen Zeichenfolge, die Sie verwendet haben, und nicht nur eine Annäherung.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X