Как узнать, содержит ли (исходный код) функцию цикл? - PullRequest
0 голосов
/ 08 января 2019

Допустим, у меня есть набор функций a, b, c, d и e, и я хочу выяснить, используют ли они непосредственно цикл:

def a():
    for i in range(3):
        print(i**2)

def b():
    i = 0
    while i < 3:
        print(i**2)
        i += 1

def c():
    print("\n".join([str(i**2) for i in range(3)]))

def d():
    print("\n".join(["0", "1", "4"]))

def e():
    "for"

Я хочу написать функцию uses_loop, чтобы можно было ожидать, что эти утверждения пройдут:

assert uses_loop(a) == True
assert uses_loop(b) == True
assert uses_loop(c) == False
assert uses_loop(d) == False
assert uses_loop(e) == False

(я ожидаю, что uses_loop(c) вернет False, потому что c использует понимание списка вместо цикла.)

Я не могу изменить a, b, c, d и e. Поэтому я подумал, что для этого можно использовать ast и пройтись по коду функции, который я получаю из inspect.getsource. Но я открыт для любых других предложений, это была только идея, как это могло бы работать.

Это насколько я пришел с ast:

def uses_loop(function):
    import ast
    import inspect
    nodes = ast.walk(ast.parse(inspect.getsource(function)))
    for node in nodes:
        print(node.__dict__)

Ответы [ 3 ]

0 голосов
/ 08 января 2019

Вы были почти там! Все, что вам нужно было сделать, это выяснить, как получить данные от объектов тела. Все они являются атрибутами после некоторого типа узла. Я просто использовал getattr(node, 'body', []), чтобы получить детей, и если любой из них имеет _ast.For или _ast.While, верните True.

Примечание : Я просто возился с кодом. Не уверен, если это где-то задокументировано и на него можно положиться. Я думаю, может быть, вы можете посмотреть? :)

def a():
    for i in range(3):
        print(i**2)

def b():
    i = 0
    while i < 3:
        print(i**2)
        i += 1

def c():
    print("\n".join([str(i**2) for i in range(3)]))

def d():
    print("\n".join(["0", "1", "4"]))

def uses_loop(function):
    import ast
    import _ast
    import inspect
    nodes = ast.walk(ast.parse(inspect.getsource(function)))
    return any(isinstance(node, (_ast.For, _ast.While)) for node in nodes)


print(uses_loop(a))    # True
print(uses_loop(b))    # True
print(uses_loop(c))    # False
print(uses_loop(d))    # False
0 голосов
/ 08 января 2019

Вам необходимо проверить, есть ли в дереве абстрактного синтаксиса функции какие-либо узлы, которые являются экземплярами ast.For или ast.While или ast.AsyncFor. Вы можете использовать ast.walk() для посещения каждого узла AST

import ast
import inspect

def uses_loop(function):
    loop_statements = ast.For, ast.While, ast.AsyncFor

    nodes = ast.walk(ast.parse(inspect.getsource(function)))
    return any(isinstance(node, loop_statements) for node in nodes)

См. Документацию для ast, async for было добавлено в 3,5 .

0 голосов
/ 08 января 2019

Если вы просто пытаетесь проверить, содержит ли тело функции ключевые слова 'for' или 'while', вы можете сделать следующее:

def uses_loop(func_name):
    import inspect
    lines = inspect.getsource(func_name)
    return 'for' in lines or 'while' in lines
...