Вот быстрый взлом, который может дать вам некоторое начало, учитывая строку текста в line
и текущий кадр стека в frame
(это, кстати, внутренняя функция traceit
) ).
import re
from types import *
def interpolatevar(matchobj):
excludetypes = set((NoneType, TypeType, FunctionType, LambdaType, ClassType,
CodeType, InstanceType, MethodType, BuiltinFunctionType,
BuiltinMethodType))
var = matchobj.group(0)
basevar = var.split(".")[0]
if basevar in frame.f_code.co_names or basevar in frame.f_code.co_varnames:
if basevar in frame.f_globals or basevar in frame.f_locals:
val = eval(var, frame.f_globals, frame.f_locals)
if type(val) not in excludetypes:
return repr(val)
return var
line = re.sub(r"[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*",
interpolatevar, line)
Поскольку для поиска идентификаторов используется регулярное выражение, он тупо находит их, даже если они в строковых литералах, но идентификатор должен фактически использоваться в функции и определяться либо в локальной, либо в глобальной области.
Он обрабатывает доступ к атрибутам объекта (например, foo.bar
будет заменен этим значением), чего не сделала бы версия, которую я первоначально разместил, и отфильтровывает значения различных типов, так как замена foo
на <function foo at 0x02793470>
didn ' Я действительно многого тебе не скажу. (Конечно, список исключений легко настраивается, если вас интересуют некоторые значения этих типов или вы можете добавить другие из модуля types
.)
В долгосрочной перспективе я думаю, что было бы выгодно взглянуть на байт-код для строки, так как легче определить, какие токены на самом деле являются идентификаторами. Модуль ast
также может быть полезен для создания дерева разбора оператора, которое позволит вам выяснить, что такое идентификаторы, но это проблематично для условий и циклов, когда вы видите только строку за раз.