使用PrettyTable打印目录中Python文件的详细信息
我正在尝试编写一个FileAnalyzer类,该类将在目录中搜索Python文件,并以PrettyTable的形式提供每个Python文件的详细信息。我对每个Python文件中的类、函数、行和字符的数量感兴趣 学习OOP的诀窍…以下是我目前掌握的代码:使用PrettyTable打印目录中Python文件的详细信息,python,function,class,oop,prettytable,Python,Function,Class,Oop,Prettytable,我正在尝试编写一个FileAnalyzer类,该类将在目录中搜索Python文件,并以PrettyTable的形式提供每个Python文件的详细信息。我对每个Python文件中的类、函数、行和字符的数量感兴趣 学习OOP的诀窍…以下是我目前掌握的代码: class FileAnalyzer: def __init__(self, directory: str) -> None: """ The files_summary
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”在我尝试运行代码时未定义。在\uuuu init\uuuu
中调用self.analyze\u files()
不足以将返回的值传递到\uuuuu init\uuu
?理想情况下,对于
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()
。同样地,将数据从文件\u summary
传递到pretty\u print
,而不向pretty\u print
传递单独的参数
编辑:
抑制错误,但
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
在pretty\u print
中,当我调用FileAnalyzer时,它不起任何作用…这个问题有点宽泛,所以很难提供一个简洁的答案。你在评论中说:
如果我在init中执行类似于[cls,funccount,codelines,characters]=self.analyze_files()
的操作,似乎也无法正确引用返回的值
虽然在风格上有点奇怪,但实际上这是非常好的语法。如果您的\uuuuu init\uuuu
方法如下所示,则它运行时不会出错:
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
}
}
然而,还有一些问题。首先,在上面的方法中,您使用的是变量名dir
,但是范围中没有这样的变量。不幸的是,dir
也是Python内置函数的名称。如果在这段代码之后插入一个断点()
,并打印self.files\u summary
的值,您将看到它的外观如下:
{<built-in function dir>: {'Number of Classes': 0, 'Number of Functions': 0, 'Number of Lines of Code': 0, 'Number of Characters': 0}}
for cls, funccount, codelines, characters in self.files_summary():
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()
但是self.files\u summary
不是函数,不能调用。它是一个字典,这也意味着在像这样的for
循环中使用它是没有意义的。由于您在\uuuu init\uuuu
中设置它的方式,它只会有一个键
如果我是你的话,我会把这个程序分解成几个部分,在把它们联系在一起之前,先让每个部分都正常工作。充分利用交互式Python提示符和调试器;使用代码中的breakpoint()
语句在使用变量之前调查变量的内容
如果我要重写您的代码,我可能会这样做:
{<built-in function dir>: {'Number of Classes': 0, 'Number of Functions': 0, 'Number of Lines of Code': 0, 'Number of Characters': 0}}
for cls, funccount, codelines, characters in self.files_summary():
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()
请注意,我已经在analyze_files
函数中删除了for
循环的多元组;没有理由对每个文件进行多次迭代。这将建立一个名为files
的实例变量,它是一个结果列表。pretty\u print
方法只是在这个列表上迭代
如果我在Python scratch目录中运行上述代码,我会得到:
+--------------------------+--------------+----------------+--------------------------------------+----------------------------------------------+
| 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 |
+--------------------------+--------------+----------------+--------------------------------------+----------------------------------------------+
您正在\uuu init\uuu
函数中设置'Number of class':cls,
,但该函数中未定义名为cls
的变量。在同一块中提到的其他变量也会出现同样的错误。@larsks是的,所以我想我的问题是,我是否可以引用我在analyze\u files
中已经定义的cls
,而不需要在analyze\u files
中附加参数。如果我在\uuu init\uuuu
中执行类似于[cls,funcount,codelines,characters]=self.analyze\u files()
的操作,似乎也没有正确引用返回的值…dir
的意思是引用目录中感兴趣的任何特定文件(在本例中是Python文件)。现在,我已将其更改为self.direct目录中的直接,但当然直接:{
中的\uuuu init\uuuu
现在返回名称错误:名称“direct”未定义
对,这与我们在注释中讨论的问题相同:该方法中没有定义名为direct
的变量。在使用变量之前必须定义它们。其思想是创建每个文件信息的实例并存储它们它在self.files\u summary
的字典中。如果我不能像函数一样调用它,那么在pretty\u print
中检索字典中的值的最佳方法是什么?我对答案做了一些更新,可能会有所帮助。