0

我正在尝试编写一个 FileAnalyzer 类,该类将在目录中搜索 Python 文件,并以 PrettyTable 的形式提供每个 Python 文件的详细信息。我对每个 Python 文件中的类、函数、行和字符的数量感兴趣。

学习 OOP 的原理......这是我到目前为止的代码:

class FileAnalyzer:
    def __init__(self, directory: str) -> None:
        """
        The files_summary attribute stores the summarized data for each Python file in the specified directory.
        """
        self.directory: str = os.listdir(directory) #Directory to be scanned

        self.analyze_files()  # summarize the python files data

        self.files_summary: Dict[str, Dict[str, int]] = {
            dir: {
                'Number of Classes': cls,
                'Number of Functions': funccount,
                'Number of Lines of Code': codelines,
                'Number of Characters': characters
            }
        }

    def analyze_files(self) -> None:
        """
        This method scans a directory for python files. For every python file, it determines the number of classes,
        functions, lines of code, and characters. The count for each one is returned in a tuple.
        """
        for dir in self.directory:
            if dir.endswith('.py'):  # Check for python files
                with open(dir, "r") as pyfile:

                    cls = 0  # Initialize classes count
                    for line in pyfile:
                        if line.startswith('Class'):
                            cls += 1

                    funccount = 0  # Initialize function count
                    for line in pyfile:
                        if line.startswith('def'):
                            funccount += 1

                    #Get number of lines of code
                    i = -1 #Account for empty files
                    for i, line in enumerate(pyfile):
                        pass

                    codelines = i + 1

                    #Get number of characters
                    characters = 0
                    characters += sum(len(line) for line in pyfile)

        return [cls, funccount, codelines, characters]


    def pretty_print(self) -> None:
        """
        This method creates a table with the desired counts from the Python files using the PrettyTable module.
        """
        pt: PrettyTable = PrettyTable(field_names=['# of Classes', '# of Functions', '# Lines of Code (Excluding Comments)',
                                                   '# of characters in file (Including Comments)'])

        for cls, funccount, codelines, characters in self.files_summary():
            pt.add_row([cls, funccount, codelines, characters])

        print(pt)

FileAnalyzer('/path/to/directory/withpythonfiles')

NameError: name 'cls' is not defined目前在我尝试运行代码时出现错误。调用self.analyze_files()inside__init__不足以将返回的值传递给__init__? 理想情况下,对于一个 python 文件

def func1():
    pass

def func2():
    pass

class Foo:
    def __init__(self):
        pass

class Bar:
    def __init__(self):
        pass


if __name__ == "__main__":
    main()

PrettyTable 会告诉我有 2 个类、4 个函数、25 行和 270 个字符。对于以下文件:

definitely not function

This is def not a function def 

PrettyTable 会告诉我该文件有 0 个函数。我想self.analyze_files()self.files_summary不将任何其他参数传递给analyze_files(). 同样,将数据从files_summaryto传递给pretty_print没有单独的参数传递给pretty_print.

编辑:

self.files_summary: Dict[str, Dict[str, int]] = {
            dir: {
                'Number of Classes': self.analyze_files()[0],
                'Number of Functions': self.analyze_files()[1],
                'Number of Lines of Code': self.analyze_files()[2],
                'Number of Characters': self.analyze_files()[3]
            }
        }

压制了错误,但是

        for self.analyze_files()[0], self.analyze_files()[1], self.analyze_files()[2], self.analyze_files()[3] in self.files_summary():
            pt.add_row([self.analyze_files()[0], self.analyze_files()[1], self.analyze_files()[2], self.analyze_files()[3]])

        return pt

当我调用 FileAnalyzer 时inpretty_print什么也没做...

4

1 回答 1

1

这个问题有点宽泛,所以很难提供一个简洁的答案。你在评论中说:

如果我[cls, funccount, codelines, characters] = self.analyze_files()init中执行类似操作,似乎也没有正确引用返回的值

虽然在风格上有点奇怪,但这实际上是非常好的语法。如果你的__init__方法看起来像这样,它运行没有错误:

    def __init__(self, directory: str) -> None:
        """
        The files_summary attribute stores the summarized data for each Python file in the specified directory.
        """
        self.directory: str = os.listdir(directory) #Directory to be scanned

        [cls, funccount, codelines, characters] = self.analyze_files()

        self.files_summary: Dict[str, Dict[str, int]] = {
            dir: {
                'Number of Classes': cls,
                'Number of Functions': funccount,
                'Number of Lines of Code': codelines,
                'Number of Characters': characters
            }
        }

但是,存在许多问题。首先,在上述方法中,您使用的是变量 name dir,但范围内没有这样的变量。不幸的是,dir它也是 Python 内置函数的名称。如果你breakpoint()在这段代码之后插入 a 并打印 的值self.files_summary,你会看到它看起来像这样:

{<built-in function dir>: {'Number of Classes': 0, 'Number of Functions': 0, 'Number of Lines of Code': 0, 'Number of Characters': 0}}

一般来说,永远不要选择隐藏 Python 内置函数的变量名,因为它可能会导致意外且难以调试的问题。如果您使用具有 Python 语法高亮支持的体面的编辑器,您将看到这些内置函数被调用,这样您就可以避免这个错误。

认为而不是dir您的意思self.directory(或只是directory,因为此时该变量在范围内)。


但还有另一个问题。

在您的pretty_print方法中,您尝试调用 self.files_summary,如下所示:

for cls, funccount, codelines, characters in self.files_summary():

self.files_summary它不是一个函数,也不是可调用的。for它是一本字典,这也意味着在这样的循环中使用它并没有真正的意义。由于您设置它的方式,它只会有一个键__init__

如果我是你,我会把这个程序分解成单独的部分,先让每个部分正常工作,然后再尝试将它们结合在一起。充分利用交互式 Python 提示符和调试器;usingbreakpoint()代码中的语句在使用变量之前调查变量的内容。


如果我要重写你的代码,我可能会这样做:

import os
import re

from prettytable import PrettyTable

re_class = re.compile(r'class')
re_def = re.compile(r'\s*def')


class FileAnalyzer:
    def __init__(self, path: str) -> None:
        self.path = path
        self.analyze_files()

    def analyze_files(self) -> None:
        self.files = []

        for entry in os.listdir(self.path):
            if not entry.endswith('.py'):
                continue

            with open(entry, "r") as pyfile:

                cls = 0
                funccount = 0
                codelines = 0
                characters = 0

                for line in pyfile:
                    codelines += 1
                    characters += len(line)

                    if re_class.match(line):
                        cls += 1
                    elif re_def.match(line):
                        funccount += 1

            self.files.append((entry, cls, funccount, codelines, characters))

    def pretty_print(self) -> None:
        pt: PrettyTable = PrettyTable(
            field_names=['Filename',
                         '# of Classes', '# of Functions',
                         '# Lines of Code (Excluding Comments)',
                         '# of characters in file (Including Comments)'])

        for path, cls, funccount, codelines, characters in self.files:
            pt.add_row([path, cls, funccount, codelines, characters])

        print(pt)


x = FileAnalyzer('.')
x.pretty_print()

请注意,我已在您的函数中删除了多个for循环;analyze_files没有理由多次遍历每个文件。这建立了一个名为的实例变量files,它是一个结果列表。该pretty_print方法只是迭代这个列表。

如果我在我的 Python 临时目录中运行上述代码,我会得到:

+--------------------------+--------------+----------------+--------------------------------------+----------------------------------------------+
|         Filename         | # of Classes | # of Functions | # Lines of Code (Excluding Comments) | # of characters in file (Including Comments) |
+--------------------------+--------------+----------------+--------------------------------------+----------------------------------------------+
|       yamltest.py        |      0       |       0        |                  30                  |                     605                      |
|       analyzer.py        |      1       |       3        |                  53                  |                     1467                     |
|         quake.py         |      0       |       0        |                  37                  |                     1035                     |
| test_compute_examples.py |      1       |       1        |                  10                  |                     264                      |
|   compute_examples.py    |      1       |       1        |                  4                   |                      82                      |
+--------------------------+--------------+----------------+--------------------------------------+----------------------------------------------+
于 2020-11-10T02:58:37.433 回答