Могу ли я получить список переменных и их типов из какого-нибудь кода python, не запуская его? - PullRequest
0 голосов
/ 18 июня 2020

У меня есть код python в строке, который я компилирую в объект кода.

Как я могу получить список имен и типов переменных без запуска кода (например, с AST?)?

Все переменные находятся в области global, хотя это также может быть полезно сделать подобное в определенных именованных функциях, например, draw().

# Example source:
source_code = """
a = 1
b = "hello world"
print(a)
print(b)
"""

code_obj = compile(source_code, "<string>", "exec")

# At this point I would like to know that 'a' is an int and 'b' is a str

exec(code_obj)

1 Ответ

0 голосов
/ 19 июня 2020

Это может быть не идеально, но оно должно делать то, что вам нужно

import ast
class ASTExplorer:
    def __init__(self, source):
        self.tree = ast.parse(source, mode="exec")
        self.result = list()

    class ASTResult:
        def __init__(self, var, expr, vType):
            self.var = var
            self.expression = expr
            self.vType = vType

    def _getLineAssignment(self, lineno):
        return next((node for node in ast.walk(self.tree) if isinstance(node, ast.Name) and node.lineno == lineno), None)

    def getVariables(self):
        for node in ast.walk(self.tree):
            if not isinstance(node, ast.Assign):
                continue
            nodeValue = node.value
            nodeVariable = self._getLineAssignment(node.lineno).id
            if(isinstance(nodeValue, ast.Constant)):
                nodeExpression = node.value.value
                self.result.append(self.ASTResult(nodeVariable, nodeExpression, type(nodeExpression)))
                continue
            elif(isinstance(nodeValue, ast.Call)):
                callFunc = nodeValue.func.id
                callArgs = "(" + (", ".join([str(x.value) for x in nodeValue.args])) + ")"
                self.result.append(self.ASTResult(nodeVariable, f"{callFunc}{callArgs}", ast.Call))
            #elif... other type handling
        return self.result

И вы бы использовали его вот так

source_code = """
myRangeVar = range(1, 10)
myIntVar = 1
myStrVar = "hello world"
myTest = fakeFunct()
myTestTwo = fakeFunct(20)
print(a)
print(b)
"""

explorer = ASTExplorer(source_code)
for result in explorer.getVariables():
    print(f"Found variable '{result.var}' with a value of '{result.expression}' (type: '{result.vType.__name__}')")

Что приводит к

Found variable 'myRangeVar' with a value of 'range(1, 10)' (type: 'Call')
Found variable 'myIntVar' with a value of '1' (type: 'int')
Found variable 'myStrVar' with a value of 'hello world' (type: 'str')
Found variable 'myTest' with a value of 'fakeFunct()' (type: 'Call')
Found variable 'myTestTwo' with a value of 'fakeFunct(20)' (type: 'Call')

Я добавил комментарий #elif... other type handling, так как в настоящее время он обрабатывает только объявления типов Constant и Call, но есть и другие, которые необходимо учитывать, если этого требует ваше решение.

...