从Python源代码中提取注释
我正试图编写一个程序来提取用户输入的代码中的注释。我试图使用正则表达式,但发现很难写 然后我找到了一个帖子。答案建议使用从Python源代码中提取注释,python,python-2.7,Python,Python 2.7,我正试图编写一个程序来提取用户输入的代码中的注释。我试图使用正则表达式,但发现很难写 然后我找到了一个帖子。答案建议使用tokenize.generate_tokens来分析语法,但是: generate_tokens()生成器需要一个参数readline,该参数 必须是可调用对象,该对象提供与 readline()内置文件对象的方法(请参阅“文件对象”一节) 但是字符串对象没有readline方法 然后我找到了另一篇帖子,建议使用StringIO.StringIO来获得readline方法。因
tokenize.generate_tokens
来分析语法,但是:
generate_tokens()
生成器需要一个参数readline
,该参数
必须是可调用对象,该对象提供与
readline()
内置文件对象的方法(请参阅“文件对象”一节)
但是字符串对象没有readline
方法
然后我找到了另一篇帖子,建议使用StringIO.StringIO
来获得readline
方法。因此,我编写了以下代码:
import tokenize
import io
import StringIO
def extract(code):
res = []
comment = None
stringio = StringIO.StringIO(code)
for toktype, tokval, begin, end, line in tokenize.generate_tokens(stringio):
# print(toknum,tokval)
if toktype != tokenize.COMMENT:
res.append((toktype, tokval))
else:
print tokenize.untokenize(toktype)
return tokenize.untokenize(res)
并输入以下代码:extract('a=1+2#a Comment')
但是得到:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "ext.py", line 10, in extract
for toktype, tokval, begin, end, line in tokenize.generate_tokens(stringio):
File "C:\Python27\lib\tokenize.py", line 294, in generate_tokens
line = readline()
AttributeError: StringIO instance has no `__call__` method
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
文件“ext.py”,第10行,摘录
对于toktype、tokval、begin、end、line in tokenize.generate_tokens(stringio):
文件“C:\Python27\lib\tokenize.py”,第294行,在generate\u tokens中
line=readline()
AttributeError:StringIO实例没有`\u调用``方法
我知道我可以编写一个新类,但有没有更好的解决方案?回答更一般的情况(从模块、函数中提取):
模块:
文档规定需要提供一个可调用函数,它公开与内置文件对象的方法相同的接口。这提示:创建一个提供该方法的对象
对于模块,我们可以将一个新模块作为一个普通文件传递给它的readline
方法。
这是关键,您传递的参数是方法readline()
给定一个小的scrpt.py
文件,其中包含:
# My amazing foo function.
def foo():
""" docstring """
# I will print
print "Hello"
return 0 # Return the value
# Maaaaaaain
if __name__ == "__main__":
# this is main
print "Main"
我们将像打开所有文件一样打开它:
fileObj = open('scrpt.py', 'r')
这个文件对象现在有一个名为readline
(因为它是一个文件对象)的方法,我们可以安全地将它传递到tokenize.generate_tokens
并创建一个生成器
(仅在Py3中--注意:Python3需要readline returnbytes
,因此需要在'rb'
模式下打开文件)返回元素的命名元组,其中包含有关标记元素的信息。下面是一个小演示:
for toktype, tok, start, end, line in tokenize.generate_tokens(fileObj.readline):
# we can also use token.tok_name[toktype] instead of 'COMMENT'
# from the token module
if toktype == tokenize.COMMENT:
print 'COMMENT' + " " + tok
注意我们是如何将fileObj.readline
方法传递给它的。现在将打印:
COMMENT # My amazing foo function
COMMENT # I will print
COMMENT # Return the value
COMMENT # Maaaaaaain
COMMENT # this is main
因此,无论位置如何,都会检测到所有注释。当然不包括docstring
功能:
对于我真的想不出的情况,如果不打开open
,也可以获得类似的结果。尽管如此,为了完整性起见,我将介绍另一种方法。在此场景中,您需要两个附加模块,和(Python3
中的):
假设您具有以下功能:
def bar():
# I am bar
print "I really am bar"
# bar bar bar baaaar
# (bar)
return "Bar"
您需要一个类似文件的对象,它有一个readline
方法来与标记化
一起使用。好的,您可以使用StringIO.StringIO
从str
创建一个类似文件的对象,并且您可以获得一个str
来表示函数的源代码。代码:
funcText = inpsect.getsource(bar)
funcFile = StringIO.StringIO(funcText)
现在我们有了一个类似文件的对象,它表示具有所需的readline
方法的函数。我们可以重新使用以前执行的循环,将fileObj.readline
替换为funcFile.readline
。我们现在得到的输出具有类似的性质:
COMMENT # I am bar
COMMENT # bar bar bar baaaar
COMMENT # (bar)
另外,如果您真的想用
re
创建一种自定义的方法,请看一看。它定义了注释的某些模式(r'.[^\r\n]*'
)名称等,使用readline
在行中循环,并在行
列表中搜索模式。谢天谢地,在您看了一会儿之后,它并不太复杂:-)
函数
提取的应答
(更新):
您已使用StringIO
创建了一个对象,该对象提供了接口,但尚未将该接口(readline
)传递给tokenize。生成\u tokens
,而是传递了完整对象(StringIO
)
此外,在else
子句中,将引发TypeError
,因为untokenize
需要一个iterable作为输入。进行以下更改后,您的函数工作正常:
def extract(code):
res = []
comment = None
stringio = StringIO.StringIO(code)
# pass in stringio.readline to generate_tokens
for toktype, tokval, begin, end, line in tokenize.generate_tokens(stringio.readline):
if toktype != tokenize.COMMENT:
res.append((toktype, tokval))
else:
# wrap (toktype, tokval) tupple in list
print tokenize.untokenize([(toktype, tokval)])
return tokenize.untokenize(res)
提供了形式为expr=extract('a=1+2#a comment')
的输入,函数将打印注释并将表达式保留在expr
中:
expr = extract('a=1+2#A comment')
#A comment
print expr
'a =1 +2 '
此外,正如我稍后提到的,Python3的
io
包含StringIO
,因此在本例中,谢天谢地,import
是不需要的。向我们展示为您提供属性错误的代码。您找到了正确的解决方案。请显示一些代码,以便我们可以帮助您使其工作。(python 3.6)将fileObject.readline直接传递给tokenize.tokenize仅在以“rb”模式打开时才起作用,因为我猜tokenize需要readline()的bytestring。@philoj确实如此。显然在所有Python>3版本中。抢手货