Python中可逆版本的compile()

3 python metaprogramming

我正在尝试在Python中创建一个与compile()相同的函数,但也让我得到原始字符串.为了消除歧义,我们将这两个函数称为comp()和decomp().那是,

a = comp("2 * (3 + x)", "", "eval")
eval(a, dict(x=3)) # => 12
decomp(a) # => "2 * (3 + x)"
Run Code Online (Sandbox Code Playgroud)

返回的字符串不必相同("2*(3 + x)"是可接受的),但它必须基本相同("2*x + 6"不会).

以下是我已经试过了工作:

  • 在compile返回的代码对象上设置属性.您无法在代码对象上设置自定义属性.
  • 子类化代码,以便我可以添加属性.代码不能被子类化.
  • 设置WeakKeyDictionary将代码对象映射到原始字符串.代码对象不能被弱引用.

这是什么工作,有问题:

  • 将文件名的原始代码字符串传递给compile().但是,我失去了实际保存文件名的能力,我也想这样做.
  • 保持一个真实的字典将代码对象映射到字符串.这会泄漏内存,但由于编译很少,因此我目前的用例是可以接受的.如果必须的话,我可能会定期通过gc.get_referrers运行密钥并杀掉死的密钥.

Mil*_*les 6

这是一个奇怪的问题,我最初的反应是,你可能最好做一些其他事情来完成你想要做的任何事情.但它仍然是一个有趣的问题,所以这是我对它的破解:我使原始代码源成为代码对象的未使用常量.

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]
Run Code Online (Sandbox Code Playgroud)
>>> a = comp('2 * (3 + x)', '', 'eval')
>>> eval(a, dict(x=3))
12
>>> decomp(a)
'2 * (3 + x)'
Run Code Online (Sandbox Code Playgroud)