У меня есть code.py
:
def funA():
print('A')
funA()
def funB():
print('B')
def funC():
print('C')
funB()
funC()
Я хочу, чтобы все функции называли себя:
funA
funC
Как написать regex
?
Ограничение:
funname(arg1, arg2,...)
lambda
, exec
) Да, я верю, что регулярное выражение было бы невозможным для соответствия случаям, указанным wim, когда сам вызов обфускается. Тем не менее, вот регулярное выражение, которое сделает funcname(...)
работу для простых самостоятельных вызовов (в форме funcname(...)
). Это регулярное выражение правильно соответствует всем тестовым случаям, изложенным в исходном вопросе:
reobj = re.compile(r"""
# Match (unreliably) Python function with self reference.
^ # Anchor to start of line.
([ \t]*) # $1: Indentation of DEF statement.
def[ \t]+ # Function definition.
([^\s(]+) # $2: Name of function to find.
.*\r?\n # Remainder of function def line.
(?: # Zero or more lines w/o funcname.
(?: # Function block lines alternatives.
\1[ \t]+ # Func block lines have extra indentation.
(?:(?!\b\2\s*\().)* # Optional non-funcname stuff on line
| [ \t]*\#.* # Allow comment lines to defy indent rules.
)? # Allow blank lines in function block.
\r?\n # End of line not containing funcname.
)* # Zero or more lines w/o funcname
\1[ \t]+ # Now match the line having funcname.
(?:(?!\b\2\s*\().)* # Optional non-funcname stuff on line
\b\2\s*\( # Match the function self reference.
""", re.MULTILINE | re.VERBOSE)
Он соответствует началу строки определения функции и фиксирует отступ пробелов перед 'def'
в группе $1
и имя функции в группе $2
. Затем он сопоставляет строки внутри функционального блока, которые не содержат имени функции, у каждого из которых есть более старшие пробелы, чем определение функции. Он пропускает пустые строки и строки, содержащие только комментарии. Он объявляет совпадение после того, как находит строку в функциональном блоке, который имеет имя функции, за которым следуют левые круглые скобки, указывая на вызов для себя. В противном случае он объявляет несоответствие, а затем продолжает поиск следующего возможного совпадения.
Обратите внимание, что это решение ненадежно и приведет к ложным срабатываниям, если имя функции происходит внутри строки или внутри комментария, следующего за другим кодом в строке. Он также не обрабатывает функции с многострочными необработанными строками. Тем не менее, он будет правильно поймать немало!
Это сложно, потому что функция может называть себя запутанными способами. Например, это так?
def funA():
print 'A'
foo = funA
foo()
funA()
Как насчет этого?
def funA():
funB()
def funB():
funA()
funA()
Или даже это?
def funA():
exec('Anuf'[::-1] + '()')
funA()
Я не думаю, что вы можете сделать это с помощью регулярного выражения.
Даже учитывая ваши новые изменения, все равно будет очень сложно, если не невозможно. Рассмотрим эту функцию, например.
def funA():
if 1 + 1 == 2:
return
funA()
Я предлагаю вам следовать совету Игнасио Васкес-Абрамса и посмотреть на аш.
Проще говоря, вы не сможете сделать это с помощью регулярного выражения. Вам нужно, по крайней мере, проанализировать определения функций, сохранить какое-то состояние в отношении того, какую функцию вы сейчас разбираете, и искать вызов текущего имени функции в рамках текущей функции.
может быть, вы можете использовать gawk, ниже мой пример кода, вам может потребоваться изменить его:
#! /usr/bin/gawk -f
{
currentLine = $0
if (currentLine ~ /def/){
inFunction = "true"
nameIndex = index($2,"(")
functionName = substr($2,1,nameIndex - 1)
#print functionName
next
}
if (inFunction == "true" && currentLine ~ functionName){
inFunction = false
print "recursive function is: " functionName
}
}
просто запустите программу, вы получите то, что хотите.
Я опишу шаблон, который, как я думаю, вам нужен: строка, начинающаяся с def, за которой следует пробел, за которым следует имя (которое вы записываете в круглых скобках), за которым следует (возможно, пустой) набор строк, начинающийся с пробела, а затем строка, начинающаяся с пробела и содержащая ваше имя функции, за которым следует открытый палец (так что вы фиксируете фактический вызов, а не только ссылку).
pass
вместо пустого тела.
def funA(): funA()
- это допустимая функция Python, которая вызывает сама себя. Если сразу за ним следует другой оператор глобальной области, не будет строк, начинающихся с пробела после определения def
. Однако описание Скоттом регулярного выражения также не соответствует этому.