2009-04-05 9 views
3

Ich versuche, eine Funktion in Python zu machen, die das Äquivalent von Kompilierung tut(), aber auch lässt mich zurück, um die Original-Zeichenkette erhalten. Nennen wir diese beiden Funktionen comp() und dispomp() für Disambiguierungszwecke. Das heißt,Reversible Version der Kompilierung() in Python

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

Der zurückgegebene String nicht über identisch ("2 * (3 + x)" akzeptabel wäre) sein, aber es muss im Grunde das gleiche ("2 * x + sein 6 "wäre nicht).

Hier ist, was ich habe versucht, dass nicht Arbeit:

  • ein Attribut Einstellung auf dem Code-Objekt durch Kompilierung zurückgegeben. Sie können keine benutzerdefinierten Attribute für Codeobjekte festlegen.
  • Subclassing-Code, so kann ich das Attribut hinzufügen. Code kann nicht unterklassifiziert werden.
  • Festlegen eines WeakKeyDictionary Mapping-Code up-Objekte auf die ursprünglichen Strings. Code-Objekte können nicht schwach referenziert werden.

Hier ist, was funktioniert, mit Fragen:

  • im ursprünglichen Code Zeichenfolge Passing für die Dateinamen() zu kompilieren. Allerdings verliere ich die Fähigkeit, dort tatsächlich einen Dateinamen zu behalten, den ich auch gerne machen würde.
  • Halten eines realen Wörterbuch Zuordnung von Code-Objekten zu Strings. Dies führt zu Speicherverlusten, obwohl das Kompilieren selten ist und für meinen aktuellen Anwendungsfall akzeptabel ist. Ich könnte die Schlüssel wahrscheinlich regelmäßig durch gc.get_referrers laufen lassen und Tote töten, wenn ich müsste.
+0

Da Sie die ursprüngliche Python Quelle haben, was ist der Sinn? –

Antwort

6

Dies ist eine Art von einem seltsamen Problem, und meine erste Reaktion ist, dass Sie besser dran sein könnten etwas ganz anderes zu tun zu tun, was auch immer es ist, Sie zu tun versuchen. Aber es ist immer noch eine interessante Frage, also hier ist mein Crack: 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)' 
4

Mein Ansatz wäre, das Code-Objekt in einem anderen Objekt zu wickeln. Etwas wie folgt aus:

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

Dann, wenn Sie den Code Objekt selbst benötigen, verwenden Sie a.compiled, und wenn Sie das Original benötigen, verwenden Sie a.original. Es kann eine Möglichkeit geben, eval dazu zu bringen, die neue Klasse so zu behandeln, als wäre sie ein gewöhnliches Codeobjekt, wobei die Funktion stattdessen auf eval (self.compiled) umgeleitet wird.

Ein Vorteil davon ist die Original-Zeichenkette wird zur gleichen Zeit wie das Code-Objekt gelöscht. Wie dem auch sei, ich denke, das Speichern der ursprünglichen Zeichenfolge ist wahrscheinlich der beste Ansatz, da Sie mit der exakten Zeichenfolge, die Sie verwendet haben, nicht nur eine Annäherung erhalten.