我正在尝试编写一个程序来提取用户输入的代码中的注释。我尝试使用正则表达式,但是发现它很难编写。
然后我在这里找到了一个帖子。答案建议用来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
我知道我可以写一个新的类,但是还有更好的解决方案吗?
该文档指定需要提供一个可调用对象,该可调用对象公开与readline()
内置文件对象方法相同的接口。这暗示:创建一个提供该方法的对象。
就模块而言,我们可以将open
一个新模块作为普通文件并传入其readline
方法。这是关键,您传递的参数是methodreadline()
。
给出一个小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
并创建一个生成器。
tokenize.generate_tokens
(仅tokenize.tokenize
在Py3中-注意: Python 3需要readline返回,bytes
因此您需要以'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
因此,无论位置如何,所有注释都会被检测到。当然不包括文档字符串。
如果没有open
我真的想不到的情况,您可以实现类似的结果。尽管如此,为了完整起见,我将提出另一种方法。在这种情况下,您将需要两个附加模块,inspect
以及StringIO
(io.StringIO
中的Python3
):
假设您具有以下功能:
def bar():
# I am bar
print "I really am bar"
# bar bar bar baaaar
# (bar)
return "Bar"
您需要一个类似文件的对象,该对象具有readline
可以与结合使用的方法tokenize
。好了,您可以使用来创建类似文件的对象,并可以str
使用来代表函数的源。在代码中:StringIO.StringIO
str
inspect.getsource(func)
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
请查看模块的源代码tokenize.py
。它定义了某些注释模式,(r'#[^\r\n]*'
)名称等,readline
并在line
列表中循环搜索样式。值得庆幸的是,经过一会儿:-),它并不太复杂。
extract
(更新):您已经创建了一个StringIO
提供接口的对象,但是还没有将接口(readline
)传递给tokenize.generate_tokens
,而是传递了完整的对象(stringio
)。
另外,在else
子句中将TypeError
引发a,因为untokenize
期望可迭代作为输入。进行以下更改,您的功能可以正常工作:
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 '
此外,正如我稍后提到的Python3io
房屋StringIO
,因此在这种情况下,import
不需要它。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句