Я предлагаю вам взглянуть на стандартный ast
модуль. Вот некоторые тривиальный код:
import ast
source = '''
x=1
if not x:
print('not x')
'''
tree = ast.parse(source)
print(ast.dump(tree))
А вот выход:
$ python test.py
Module(body=[Assign(targets=[Name(id='x', ctx=Store())], value=Num(n=1)), If(test=UnaryOp(op=Not(), operand=Name(id='x', ctx=Load())), body=[Expr(value=Call(func=Name(id='print', ctx=Load()), args=[Str(s='not x')], keywords=[]))], orelse=[])])
Эли Бендерский написал article по работе с AST, и он включает в себя некоторые примеры кода для посещения Узлы АСТ. Вы хотели бы посетить, где искали конкретные конструкции. В приведенном выше примере вы будете искать (суб) выражения под узлом If
, где операнд либо был непосредственно обработан как логический, либо рассматривался как единственный операнд для узла Not()
.
Поиск любого возможного случая может быть довольно сложным. Но я думаю, что вы можете легко найти «простые» случаи (если x, если не x, если x или y) со страницей или двумя кодами.
EDIT: Вот код, который (я думаю) делает то, что вы хотите.
import ast
source = '''#Line 1
x=1
y=2
if not x:
print('not x')
if y is None:
print('y is none')
while y or not x or (x < 1 and not y and x < 10):
print('x < 10')
x += 1
'''
tree = ast.parse(source)
class FindNameAsBoolean(ast.NodeVisitor):
def __init__(self, lines):
self.source_lines = lines
def report_find(self, kind, locn, size=3):
print("\nFound %s at %s" % (kind, locn))
print(self.source_lines[locn[0]-1])
print(' ' * locn[1], '^' * size, sep='')
def visit_UnaryOp(self, node):
if isinstance(node.op, ast.Not) and isinstance(node.operand, ast.Name):
self.report_find('NOT-NAME', (node.lineno, node.col_offset), size=4 + len(node.operand.id))
self.generic_visit(node)
def visit_BoolOp(self, node):
opname = type(node.op).__name__.upper()
for kid in node.values:
if isinstance(kid, ast.Name):
self.report_find('%s-NAME' % opname, (node.lineno, node.col_offset), size=len(kid.id))
self.generic_visit(node)
class FindTests(ast.NodeVisitor):
def __init__(self, lines):
self.source_lines = lines
def _fnab(self, node):
cond = node.test
FindNameAsBoolean(self.source_lines).visit(cond)
def visit_If(self, node):
self._fnab(node)
self.generic_visit(node)
def visit_While(self, node):
self._fnab(node)
self.generic_visit(node)
FindTests(source.splitlines()).visit(tree)
И вот результат:
$ python test.py
Found NOT-NAME at (5, 3)
if not x:
^^^^^
Found OR-NAME at (12, 6)
while y or not x or (x < 1 and not y and x < 10):
^
Found NOT-NAME at (12, 11)
while y or not x or (x < 1 and not y and x < 10):
^^^^^
Found NOT-NAME at (12, 31)
while y or not x or (x < 1 and not y and x < 10):
^^^^^
Вы знаете, что 'x или y' является своего рода гибридом - входы * оцениваются * как логические, но * вывод * полного выражения будет одним из входов (а не булевым). –