Обратимая версия compile() в Python

Я пытаюсь создать функцию на Python, которая делает эквивалент compile(), но также позволяет мне вернуть исходную строку. Давайте назовем эти две функции comp() и decomp() для устранения неоднозначности. То есть,

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

Возвращаемая строка не обязательно должна быть идентичной ("2*(3+x)" будет приемлемо), но она должна быть в основном одинаковой ("2 * x + 6" не будет ).

Вот что я пробовал, но не работает:

  • Установка атрибута объекта кода, возвращаемого компиляцией. Вы не можете установить настраиваемые атрибуты для объектов кода.
  • Код подкласса, чтобы я мог добавить атрибут. код не может быть подклассом.
  • Настройка сопоставления объектов кода WeakKeyDictionary с исходными строками. объекты кода не могут иметь слабую ссылку.

Вот что работает с проблемами:

  • Передача исходной строки кода для имени файла в compile(). Однако я теряю возможность фактически хранить там имя файла, что я тоже хотел бы сделать.
  • Сохранение реального словаря, отображающего объекты кода в строки. Это приводит к утечке памяти, хотя, поскольку компиляция выполняется редко, это приемлемо для моего текущего варианта использования. Вероятно, я мог бы периодически запускать ключи через gc.get_referrers и убивать мертвые, если бы мне пришлось.

person Community    schedule 05.04.2009    source источник
comment
Поскольку у вас есть исходный код Python, в чем смысл?   -  person S.Lott    schedule 05.04.2009


Ответы (2)


Это своего рода странная проблема, и моя первоначальная реакция такова, что вам, возможно, лучше заняться чем-то совершенно другим, чтобы выполнить то, что вы пытаетесь сделать. Но это все еще интересный вопрос, поэтому вот моя попытка: я делаю исходный код неиспользуемой константой объекта кода.

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)'
person Miles    schedule 05.04.2009

Мой подход состоял бы в том, чтобы обернуть объект кода в другой объект. Что-то вроде этого:

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

Затем всякий раз, когда вам нужен сам объект кода, вы используете a.compiled, а всякий раз, когда вам нужен оригинал, вы используете a.original. Может быть способ заставить eval обрабатывать новый класс, как если бы это был обычный объект кода, вместо этого перенаправляя функцию на вызов eval(self.compiled).

Одним из преимуществ этого является то, что исходная строка удаляется одновременно с объектом кода. Как бы вы это ни делали, я думаю, что сохранение исходной строки, вероятно, является лучшим подходом, так как вы получите точную строку, которую вы использовали, а не просто приближение.

person Silverfish    schedule 05.04.2009