Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/307.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何找到没有返回语句的Python方法?_Python_Static Analysis - Fatal编程技术网

如何找到没有返回语句的Python方法?

如何找到没有返回语句的Python方法?,python,static-analysis,Python,Static Analysis,我非常喜欢对象的方法(修改objects属性)返回self,以便可以链接方法调用。例如: boundingBox.grow(0.05).shift(x=1.3) 而不是 boundingBox.grow(0.05) boundingBox.shift(x=1.3) 我想搜索我以前项目的代码来调整这个模式。如何找到没有返回语句的方法 理想情况下,我希望让一个程序在一个文件夹上运行。该程序搜索Python文件、查找类、检查它们的方法并搜索返回语句。如果没有返回语句,它将输出文件名、类名称和方法名

我非常喜欢对象的方法(修改objects属性)返回
self
,以便可以链接方法调用。例如:

boundingBox.grow(0.05).shift(x=1.3)
而不是

boundingBox.grow(0.05)
boundingBox.shift(x=1.3)
我想搜索我以前项目的代码来调整这个模式。如何找到没有返回语句的方法


理想情况下,我希望让一个程序在一个文件夹上运行。该程序搜索Python文件、查找类、检查它们的方法并搜索返回语句。如果没有返回语句,它将输出文件名、类名称和方法名称。

您可以使用ast获取名称,我将努力获取行号:

import inspect
import importlib
import ast

class FindReturn(ast.NodeVisitor):
    def __init__(self):
        self.data = []

    def visit_ClassDef(self,node):
        self.data.append(node.name)
        self.generic_visit(node)

    def visit_FunctionDef(self, node):
        if not any(isinstance(n, ast.Return) for n in node.body):
            self.data.append(node.name)
        self.generic_visit(node)

mod = "test"
mod = importlib.import_module(mod)
p = ast.parse(inspect.getsource(mod))

f = FindReturn()
f.visit(p)

print(f.data)
输入:

class Foo(object):
    def __init__(self):
        self.foo = "foo"

    def meth1(self):
        self.bar = "bar"

    def meth2(self):
        self.foobar = "foobar"


    def meth3(self):
        self.returns = "foobar"
        return self.returns

class Bar(object):
    def __init__(self):
        self.foo = "foo"

    def meth1(self):
        self.bar = "bar"

    def meth2(self):
        self.foobar = "foobar"


    def meth3(self):
        self.returns = "foobar"
        return self.returns
输出:

['Foo', '__init__', 'meth1', 'meth2', 'Bar', '__init__', 'meth1', 'meth2']
{<module 'test' from '/home/padraic/test.pyc'>: defaultdict(None, {'class_Foo': {'methods': ['meth1', 'meth2']}, 'class_Bar': {'methods': ['meth1', 'meth2']}})}
{'test.py': defaultdict(None, {'class_Foo': {'methods': [{'meth': 'meth1', 'line': 6}, {'meth': 'meth2', 'line': 9}]}, 'class_Bar': {'methods': [{'meth': 'meth1', 'line': 21}, {'meth': 'meth2', 'line': 24}]}}),
 'test2.py': defaultdict(None, {'class_Test2': {'methods': [{'meth': 'test1', 'line': 2}, {'meth': 'test2', 'line': 5}]}})}
{'test.py': defaultdict(None, {'class_Foo': {'methods': [{'meth': 'meth1', 'line': 7},
 {'meth': 'meth2', 'line': 10}]}, 'class_Bar': {'methods': [{'meth': 'meth1', 'line': 22}, {'meth': 'meth2', 'line': 25}]}}),
 'test2.py': defaultdict(None, {'class_Test2': {'methods': [{'meth': 'test1', 'line': 3}, {'meth': 'test2', 'line': 8}]}})}
这里的文件名显然是
“test.py”

这可能是一种更好的数据分组方法:

import inspect
import importlib
import ast
from collections import defaultdict

mod = "test"
mod = importlib.import_module(mod)
p = ast.parse(inspect.getsource(mod))



data = defaultdict(defaultdict)
classes = [cls for cls in p.body if isinstance(cls, ast.ClassDef)]
for cls in classes:
    name = "class_{}".format(cls.name)
    data[mod][name] = {"methods": []}
    for node in cls.body:
        if not any(isinstance(n, ast.Return) for n in node.body):
            if node.name != "__init__":
                data[mod][name]["methods"].append(node.name)
输出:

['Foo', '__init__', 'meth1', 'meth2', 'Bar', '__init__', 'meth1', 'meth2']
{<module 'test' from '/home/padraic/test.pyc'>: defaultdict(None, {'class_Foo': {'methods': ['meth1', 'meth2']}, 'class_Bar': {'methods': ['meth1', 'meth2']}})}
{'test.py': defaultdict(None, {'class_Foo': {'methods': [{'meth': 'meth1', 'line': 6}, {'meth': 'meth2', 'line': 9}]}, 'class_Bar': {'methods': [{'meth': 'meth1', 'line': 21}, {'meth': 'meth2', 'line': 24}]}}),
 'test2.py': defaultdict(None, {'class_Test2': {'methods': [{'meth': 'test1', 'line': 2}, {'meth': 'test2', 'line': 5}]}})}
{'test.py': defaultdict(None, {'class_Foo': {'methods': [{'meth': 'meth1', 'line': 7},
 {'meth': 'meth2', 'line': 10}]}, 'class_Bar': {'methods': [{'meth': 'meth1', 'line': 22}, {'meth': 'meth2', 'line': 25}]}}),
 'test2.py': defaultdict(None, {'class_Test2': {'methods': [{'meth': 'test1', 'line': 3}, {'meth': 'test2', 'line': 8}]}})}
其中test2包含:

class Test2:
    def test1(self):
        pass

    def test2(self):
        self.f=4
        s = self.test_return()
        i = 3

    def test_return(self):
        return "Test2"
可以使用node.lineno获取方法定义之前的行:

classes = [cls for cls in p.body if isinstance(cls, ast.ClassDef)]
    for cls in classes:
        name = "class_{}".format(cls.name)
        data[py][name] = {"methods": []}
        for node in cls.body:
            if not any(isinstance(n, ast.Return) for n in node.body):
                if node.name != "__init__":
                    data[py][name]["methods"].append({"meth":node.name,"line":node.lineno})
输出:

['Foo', '__init__', 'meth1', 'meth2', 'Bar', '__init__', 'meth1', 'meth2']
{<module 'test' from '/home/padraic/test.pyc'>: defaultdict(None, {'class_Foo': {'methods': ['meth1', 'meth2']}, 'class_Bar': {'methods': ['meth1', 'meth2']}})}
{'test.py': defaultdict(None, {'class_Foo': {'methods': [{'meth': 'meth1', 'line': 6}, {'meth': 'meth2', 'line': 9}]}, 'class_Bar': {'methods': [{'meth': 'meth1', 'line': 21}, {'meth': 'meth2', 'line': 24}]}}),
 'test2.py': defaultdict(None, {'class_Test2': {'methods': [{'meth': 'test1', 'line': 2}, {'meth': 'test2', 'line': 5}]}})}
{'test.py': defaultdict(None, {'class_Foo': {'methods': [{'meth': 'meth1', 'line': 7},
 {'meth': 'meth2', 'line': 10}]}, 'class_Bar': {'methods': [{'meth': 'meth1', 'line': 22}, {'meth': 'meth2', 'line': 25}]}}),
 'test2.py': defaultdict(None, {'class_Test2': {'methods': [{'meth': 'test1', 'line': 3}, {'meth': 'test2', 'line': 8}]}})}
或者,我们可以通过从正文中的最后一个arg获取行号来猜测缺少返回的位置:

data[py][name]["methods"].append({"meth":node.name,"line": node.body[-1].lineno})
输出:

['Foo', '__init__', 'meth1', 'meth2', 'Bar', '__init__', 'meth1', 'meth2']
{<module 'test' from '/home/padraic/test.pyc'>: defaultdict(None, {'class_Foo': {'methods': ['meth1', 'meth2']}, 'class_Bar': {'methods': ['meth1', 'meth2']}})}
{'test.py': defaultdict(None, {'class_Foo': {'methods': [{'meth': 'meth1', 'line': 6}, {'meth': 'meth2', 'line': 9}]}, 'class_Bar': {'methods': [{'meth': 'meth1', 'line': 21}, {'meth': 'meth2', 'line': 24}]}}),
 'test2.py': defaultdict(None, {'class_Test2': {'methods': [{'meth': 'test1', 'line': 2}, {'meth': 'test2', 'line': 5}]}})}
{'test.py': defaultdict(None, {'class_Foo': {'methods': [{'meth': 'meth1', 'line': 7},
 {'meth': 'meth2', 'line': 10}]}, 'class_Bar': {'methods': [{'meth': 'meth1', 'line': 22}, {'meth': 'meth2', 'line': 25}]}}),
 'test2.py': defaultdict(None, {'class_Test2': {'methods': [{'meth': 'test1', 'line': 3}, {'meth': 'test2', 'line': 8}]}})}
使用iglob忽略其他文件也可能更好:

import glob
for py in glob.iglob(os.path.join(path,"*.py")):
    with open(os.path.join(path, py)) as f:
        p = ast.parse(f.read(), "", "exec")

Python基于缩进的块结构使自己编写这样的程序变得非常容易。只需查找
def(…):
并检查以下块的最后一行,查看它是否有
返回值
。分配每个方法的调用,并测试它是否为
。您可以在函数中的任何点上有
返回值
,但是如果最后一行没有,那么在某些情况下它不会返回任何内容,我相信这就是您想要报告的内容。我不能责怪你寻找一个现有的工具,只是提供一个备份建议。有些人太习惯JavaScript了,我不认为pep8中有任何具体的东西。。。但我个人认为它看起来很恶心。。。对于像
set\u size
这样的东西,返回
self
在python中通常是一种非常不标准的约定……行号只是
节点。对于
FunctionDef
节点,lineno
。@tzaman,是的,干杯。实际上,我的意思是缺少返回,但不确定这有多容易。嗯,也许是
max(在ast.walk(node)中n.lineno代表n)
?@tzaman,从作业中获取
AttributeError
。我不确定这是否是一个需求,但我确信它是可行的。啊,您可能需要使用
if getattr()