首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Python review_generator

Python review_generator
EN

Code Review用户
提问于 2014-12-13 15:44:28
回答 3查看 636关注 0票数 26

如果一个Python文件包含一个用糟糕的风格编写的Python脚本,这个脚本将输出一个纠正其问题的评论。

代码语言:javascript
复制
RESERVED_KEYWORDS=['abs','dict','help','min','setattr','all','dir','hex','next','slice',
'any','divmod','id','object','sorted','ascii','enumerate','input','oct',
'staticmethod','bin','eval','int','open','str','bool','exec','isinstance',
'ord','sum','bytearray','filter','issubclass','pow','super','bytes','float',
'iter','print','tuple','callable','format','len','property','type','chr',
'frozenset','list','range','vars','classmethod','getattr','locals','repr','zip',
'compile','globals','map','reversed',
'__import__','complex','hasattr','max','round','delattr','hash','memoryview','set']

FILENAME = "code_with_bad_style.py"

BULTIN_REASSIGNED_ERROR = """You wrote:

    {} = "something"

That is not good because {} is a built-in in Python
and you should never re-assign new values to the
built-ins, in case you are wondering wheter a word is a builtin or
not go to https://docs.python.org/3/library/functions.html to read the
complete list"""

NAME_NOT_USED_ERROR="""You should use

    if __name__ == "__name__":
        main()
So that your file is going to usable as both
a stand-alone programme and an importable programme.
"""

NO_DOCS_ERROR = """You should consider using some docstrings.
Docstrings are multiline comments that explain what a function does,
they are of great help for the reader. They look like the following:

    def function(a, b):
        \"\"\"Do X and return a list.\"\"\"
"""

USE_LIST_COMPREHENSION_ERROR = """In python there is
a very powerful language feature called [list comprehension][https://docs.python.org/2/tutorial/datastructures.html#list-comprehensions].
The following:

    result = []
    for i in lst:
        if foo(i):
            result.append(bar(i))

should be replaced with:

    result = [bar(i) for i in lst if foo(i)]
"""

USE_WITH_ERROR = """There is a very convenient way of handling files in python:
the with statement. It handles closing automatically so you do not
have to worry about it. It is very simple to use, look at the following example:

    with open("x.txt") as f:
        data = f.read()
        # do something with data
"""

PRINT_BREAKING_PYTHON_3_ERROR = """You should use parenthesis with your print
statement (in Python 3 it is a function) to keep compatibility with python 3"""

IMPORT_STAR_ERROR = """You should avoid using:

    from long_long_long_name import *

because people will not know where you are taking your functions from.
Instead use:

    import long_long_long_name as short
"""

SEPARATOR = """
----------

"""

def nextline(line,lines):
    return lines[lines.index(line) + 1]

def reassign_built_in_error(code):
    for built_in in RESERVED_KEYWORDS:
        if built_in + " =" in code or built_in + "=" in code:
            return BULTIN_REASSIGNED_ERROR.format(built_in,built_in)

def if_name_error(code):
    if "__name__" not in code:
        return NAME_NOT_USED_ERROR

def no_docs_error(code):
    for line in code.splitlines():
        if line.startswith("def") or line.startswith("class"):
            if '"""' not in nextline(line,code):
                return NO_DOCS_ERROR

def use_list_comprehension_error(code):
    if "append" in code:
        return USE_LIST_COMPREHENSION_ERROR

def with_not_used_error(code):
    if ".close()" in code and ".open()" in code:
        return USE_WITH_ERROR

def print_breaking_3_error(code):
    for line in code.splitlines():
        if line.startswith("print") and "(" not in line:
            return PRINT_BREAKING_PYTHON_3_ERROR

def import_star_error(code):
    for line in code.splitlines():
        if line.startswith("import") and "*" not in line:
            return IMPORT_STAR_ERROR

def main():
    ALL_ANALYSIS = [reassign_built_in_error,if_name_error,no_docs_error,
                    use_list_comprehension_error,with_not_used_error,
                    print_breaking_3_error,import_star_error]

    with open(FILENAME) as f:
        code = f.read()

    for analysis in ALL_ANALYSIS:
        result = analysis(code)
        if result:
            print(result)
            print(SEPARATOR)

if __name__ == "__main__":
    main()
EN

回答 3

Code Review用户

回答已采纳

发布于 2014-12-13 16:18:12

这段代码甚至连基本的PEP8测试都没有通过。这听起来有点虚伪-)

使用文件中硬编码的目标文件名,此脚本非常不方便:

文件名= "code_with_bad_style.py“

看看析解析解析,让这个脚本在命令行中使用一个文件名参数。

有几种方法将代码拆分为多行,例如:

def no_docs_error(代码):用于code.splitlines():#.def print_breaking_3_error(代码):用于code.splitlines():#.

使用更大的源代码,这可能是非常浪费。最好在开始时将其拆分成一行,并将列表传递给需要列表的方法,而不是单个字符串版本。

这张支票完全错了:

if line.startswith("import") and "\*" not in line: return IMPORT\_STAR\_ERROR

..。因此,这将与这种说法相匹配:

代码语言:javascript
复制
import re

..。这与这样的说法不相符:

代码语言:javascript
复制
from long_long_long_name import *

放弃“不”是不够的,因为这条规则不符合你想要阻止的东西。

在这个规则和许多其他规则中,最好使用正则表达式。例如,使用其他全局值在文件的顶部定义:

代码语言:javascript
复制
RE_IMPORT_STAR = re.compile(r'^from .* import \*')

然后做这样的检查:

代码语言:javascript
复制
if RE_IMPORT_STAR.match(line):
    return IMPORT_STAR_ERROR

许多其他测试可以使用regexes,以获得更好的判断力,而且通常也可以更好的性能。

您定义的规则有时过于宽松,例如:

对于代码中的built_in :如果代码中的built_in + "=“或代码中的built_in +”=“:返回BULTIN_REASSIGNED_ERROR.format(built_in,built_in)

这将无法与以下内容相匹配:

代码语言:javascript
复制
abs     = 'hello'

同时,同样的规则使得这个完全有效的代码失败:

代码语言:javascript
复制
parser.add_argument('-a', '--alphabet',
                    help='override the default alphabet')

在这段代码中还有许多类似的例子。

票数 22
EN

Code Review用户

发布于 2014-12-13 15:54:33

My Review:

在编写程序时,他们应该能够接受命令行参数,指定要处理的输入文件(S)。您的程序对名称进行硬编码以要求code_with_bad_style.py

您的许多函数会立即拆分行并处理它们。由于此工作多次重复,您可能应该将行拆分一次,然后,而不是传递原始代码,而是传递行。

此外,对于类似UNIX的约定,如果命令行上没有提供文件,则应该处理STDIN。

另一方面,当我想到这一点的时候,它对我很管用。

自评

python code_with_bad_style.py

您应该考虑使用一些文档字符串。

Docstring是多行注释,可以解释函数的功能,它们对读者有很大的帮助。它们看起来如下:

代码语言:javascript
复制
def function(a, b):
    """Do X and return a list."""

在python中有一个非常强大的语言特性,名为列表理解

以下内容如下:

1: if (I):result.append(bar(i))中的i= []

应改为:

代码语言:javascript
复制
result = [bar(i) for i in lst if foo(i)]

在python中有一种非常方便的处理文件的方法: with语句。它自动处理关闭,所以您不必担心它。使用起来非常简单,请看下面的示例:

代码语言:javascript
复制
with open("x.txt") as f:
    data = f.read()
    # do something with data
票数 17
EN

Code Review用户

发布于 2016-06-01 23:49:23

您的代码可以通过使用ast模块进行简化和更正。唯一不能在ast级别重新实现的错误是“使用没有括号的打印”。

示例代码检查"Reassinged builtin“、”缺失docstring“和__name__ (未使用):

代码语言:javascript
复制
import ast
import builtins
BUILTIN_NAMES = [name for name in dir(builtins) if not name.startswith("_")]
class ErrorChecker(ast.NodeVisitor):
     def __init__(self):
         self.errors = set()
         self.name_used = False
     def visit_Name(self, node):
          if node.id in BUILTIN_NAMES and isinstance(node.ctx,ast.Store):
               self.errors.add(BUILTIN_REASSIGNED_ERROR)
          elif node.id == "__name__":
               self.name_used = True
     def visit_FunctionDef(self, node):
          if not ast.get_docstring(node):
              self.errors.add(NO_DOCS_ERROR)
          self.generic_visit(node)
     visit_ClassDef = visit_FunctionDef

其他错误检查器都可以在AST级别上类似地实现,这将使它们不会意外地捕获字符串文本中的坏语句或被字符串文本混淆。

如果使用python解析器/编译器,也可以检查“打印函数周围缺少的括号”错误:

代码语言:javascript
复制
import __future__
try:
   compile(code, FILENAME, "exec", __future__.print_function.compiler_flag)
except SyntaxError:
   #Code uses print without parentheses
票数 8
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/73554

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档