百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

编写更多 pythonic 代码(十一)——Python 代码质量分析工具

lipiwang 2025-05-28 18:30 11 浏览 0 评论


在本文中,我们将确定高质量的 Python 代码,并向您展示如何提高您自己的代码质量。

我们将分析和比较可用于将代码提升到新水平的工具。无论你是已经使用Python一段时间了,还是刚刚开始,你都可以从这里讨论的实践和工具中受益。

什么是代码质量?

当然,你想要高质量的代码,谁不会呢?但是为了提高代码质量,我们必须定义它是什么。

快速的谷歌搜索会产生许多定义代码质量的结果。事实证明,这个词对人们来说可能意味着许多不同的事情。

尝试定义代码质量的一种方法是就以下高质量的代码标识符达成一致:

  • 它做了它应该做的事情。
  • 它不包含缺陷或问题。
  • 它易于阅读、维护和扩展。

这三个标识符虽然简单化,但似乎已得到普遍同意。为了进一步扩展这些想法,让我们深入研究为什么每个想法在软件领域都很重要。

为什么代码质量很重要?

为了确定为什么高质量代码很重要,让我们重新审视这些标识符。我们将看到当代码不符合它们时会发生什么。

它没有做它应该做的事情

满足要求是任何产品、软件或其他方面的基础。我们制作软件来做某事。如果最后,它没有做到...好吧,这绝对不是高质量的。

它确实包含缺陷和问题

如果您正在使用的东西有问题或给您带来问题,您可能不会称之为高质量。事实上,如果它足够糟糕,你可能会完全停止使用它。

为了不使用软件作为示例,假设您的真空吸尘器在普通地毯上效果很好。它可以清除所有的灰尘和猫毛。一个决定命运的夜晚,猫撞倒了一株植物,到处都是泥土。当您尝试使用真空清洁污垢堆时,它会破裂,将污垢喷到各处。

虽然真空在某些情况下有效,但它不能有效地处理偶尔的额外负载。因此,您不会称其为高质量的真空吸尘器。

这是我们希望在代码中避免的问题。如果事情在边缘情况下出现问题并且缺陷导致不必要的行为,我们就没有高质量的产品。

难以阅读、维护或扩展

想象一下:客户请求一项新功能。编写原始代码的人已经走了。替换它们的人现在必须理解已经存在的代码。那个人就是你。

如果代码易于理解,您将能够分析问题并更快地提出解决方案。如果代码复杂且令人费解,则可能需要更长的时间,并可能做出一些错误的假设。

如果在不中断以前功能的情况下轻松添加新功能也很好。如果代码容易扩展,您的新功能可能会破坏其他内容。

没有人愿意处于必须阅读、维护或扩展低质量代码的位置。这意味着每个人的头痛和更多的工作。

你必须处理低质量的代码,但不要让其他人处于同样的情况,这已经够糟糕的了。您可以提高所编写代码的质量。

如果您与开发人员团队合作,则可以开始实施方法以确保更好的整体代码质量

如何提高 Python 代码质量

在我们的高质量代码之旅中,需要考虑一些事项。首先,这一旅程不是纯粹客观的旅程。对于高质量代码的外观,有一些强烈的感觉。

虽然每个人都希望就上述标识符达成一致,但他们实现的方式是一条主观的道路。当您谈论实现可读性、维护和可扩展性时,通常会出现最固执己见的主题。

所以请记住,虽然本文将始终保持客观,但在代码方面有一个非常固执己见的世界。

所以,让我们从最固执己见的话题开始:代码风格。

python风格指南

古老的问题开始:空格还是制表符?

无论您个人对如何表示空格的看法如何,都可以安全地假设您至少希望代码保持一致性。

风格指南用于定义编写代码的一致方式。通常,这都是表面的,这意味着它不会改变代码的逻辑结果。虽然,一些风格选择确实避免了常见的逻辑错误。

样式指南有助于实现使代码易于阅读、维护和扩展的目标

就Python而言,有一个被广泛接受的标准。它部分是由Python编程语言本身的作者编写的。

PEP 8 为 Python 代码提供了编码约定。Python 代码遵循此风格指南是相当常见的。这是一个很好的起点,因为它已经明确定义。

作为 Python 增强提案的姊妹篇,PEP 257 描述了 Python 文档字符串的约定,这些文档字符串是用于记录模块、类、函数和方法的字符串。作为额外的好处,如果文档字符串是一致的,有一些工具能够直接从代码生成文档。

所有这些指南所做的只是定义一种设置代码样式的方法。但是你如何执行它呢?那么代码中的缺陷和问题呢,你怎么能检测这些呢?这就是Linter的用武之地。

Linters

什么是Linter?

首先,让我们谈谈Linter。小错误、风格不一致和危险的逻辑不会让你的代码感觉很好。

但我们都会犯错。你不能指望自己总是及时抓住它们。键入错误的变量名称,忘记右括号,Python中的制表符不正确,使用错误的参数数调用函数,列表不胜枚举。Linter有助于识别这些问题区域。

此外,大多数编辑器和 IDE 能够在您键入时在后台运行 linter。这将生成一个能够在运行代码之前突出显示、下划线或以其他方式识别代码中的问题区域的环境。它就像对代码的高级拼写检查。它用波浪形的红线突出显示问题,就像您最喜欢的文字处理器一样。

Linter分析代码以检测各种类别的Linter。这些类别可以大致定义为以下内容:

  1. 逻辑Linter代码错误具有潜在意外结果的代码危险的代码模式
  2. 文体Linter代码不符合定义的约定

还有一些代码分析工具可提供对代码的其他见解。虽然根据定义可能不是linters,但这些工具通常与linters并排使用。他们也希望提高代码的质量。

最后,还有一些工具可以自动将代码格式化为某些规范。这些自动化工具确保我们劣等的人类思维不会搞砸惯例。

我的 Python 的 linter 选项是什么?

在深入研究您的选择之前,重要的是要认识到一些“linters”只是多个Linter很好地包装在一起。这些组合linters的一些流行示例如下:

Flake8:能够检测逻辑和风格上的Linter。它将 pycodestyle 的样式和复杂性检查添加到 PyFlakes 的逻辑 lint 检测中。它结合了以下Linter:

  • PyFlakes
  • pycodestyle(以前称为PEP8)
  • Mccabe

Pylama:由大量Linter和其他用于分析代码的工具组成的代码审计工具。它结合了以下内容:

  • pycodestyle(以前称为PEP8)
  • Pydocstyle(原PEP257)
  • PyFlakes
  • Mccabe
  • Pylint
  • Radon
  • gjslint

以下是一些独立的linters,并附有简要说明:

linter

类别

描述

Pylint

逻辑与风格

检查错误,尝试执行编码标准,查找代码异味

PyFlakes

逻辑

分析程序并检测各种错误

pycodestyle

文体

检查 PEP 8 中的一些样式约定

pydocstyle

文体

检查是否符合 Python 文档字符串约定

Bandit

逻辑

分析代码以查找常见的安全问题

MyPy

逻辑

检查可选强制的静态类型

以下是一些代码分析和格式化工具:

工具

类别

描述

Mccabe

分析

检查Mccabe的复杂性

Radon

分析

分析代码中的各种指标(代码行数、复杂性等)

Black

格式化程序

不折不扣地格式化 Python 代码

Isort

格式化程序

通过按字母顺序排序并分成几个部分来设置导入格式

对比 Python Linters

让我们更好地了解不同的linters能够捕捉到什么以及输出是什么样子的。为此,我使用默认设置在几个不同的 linters 中运行相同的代码。

我通过linters运行的代码如下。它包含各种逻辑和文体问题:

Bash
"""
code_with_lint.py
Example Code with lots of lint!
"""
import io
from math import *


from time import time
some_global_var = 'GLOBAL VAR NAMES SHOULD BE IN ALL_CAPS_WITH_UNDERSCOES'

def multiply(x, y):
    """
    This returns the result of a multiplation of the inputs
    """
    some_global_var = 'this is actually a local variable...'
    result = x* y
    return result
    if result == 777:
        print("jackpot!")

def is_sum_lucky(x, y):
    """This returns a string describing whether or not the sum of input is lucky
    This function first makes sure the inputs are valid and then calculates the
    sum. Then, it will determine a message to return based on whether or not
    that sum should be considered "lucky"
    """
    if x != None:
        if y is not None:
            result = x+y;
            if result == 7:
                return 'a lucky number!'
            else:
                return( 'an unlucky number!')

            return ('just a normal number')

class SomeClass:

    def __init__(self, some_arg,  some_other_arg, verbose = False):
        self.some_other_arg  =  some_other_arg
        self.some_arg        =  some_arg
        list_comprehension = [((100/value)*pi) for value in some_arg if value != 0]
        time = time()
        from datetime import datetime
        date_and_time = datetime.now()
        return


下面的比较显示了我使用的 linters 及其用于分析上述文件的运行时。我应该指出,这些并不完全可比,因为它们服务于不同的目的。例如,PyFlakes不会像Pylint那样识别风格错误。

linter

命令

时间

Pylint

Pylintcode_with_lint.py

1.16秒

PyFlakes

code_with_lint.py

0.15秒

pycodestyle

pycodestyle code_with_lint.py

0.14秒

pydocstyle

pydocstyle code_with_lint.py

0.21秒

有关每个输出,请参阅以下部分。

Pylint

Pylint是大约在2006年诞生,并且仍然维护良好。它已经存在了足够长的时间,贡献者已经修复了大多数主要的错误,而且核心功能也发展得很好。

对Pylint的普遍抱怨是,它很慢,默认情况下过于冗长,而且需要大量的配置才能让它按照你想要的方式工作。撇开速度不谈,其他的抱怨在某种程度上是一把双刃剑。啰嗦可能是因为彻底性。大量的配置可能意味着对你的喜好有很多适应性。

闲话少说,在上面的代码中运行Pylint后的输出:

Bash
No config file found, using default configuration
*** Module code_with_lint
W: 23, 0: Unnecessary semicolon (unnecessary-semicolon)
C: 27, 0: Unnecessary parens after 'return' keyword (superfluous-parens)
C: 27, 0: No space allowed after bracket
                return( 'an unlucky number!')
                      ^ (bad-whitespace)
C: 29, 0: Unnecessary parens after 'return' keyword (superfluous-parens)
C: 33, 0: Exactly one space required after comma
    def __init__(self, some_arg,  some_other_arg, verbose = False):
                               ^ (bad-whitespace)
C: 33, 0: No space allowed around keyword argument assignment
    def __init__(self, some_arg,  some_other_arg, verbose = False):
                                                          ^ (bad-whitespace)
C: 34, 0: Exactly one space required around assignment
        self.some_other_arg  =  some_other_arg
                             ^ (bad-whitespace)
C: 35, 0: Exactly one space required around assignment
        self.some_arg        =  some_arg
                             ^ (bad-whitespace)
C: 40, 0: Final newline missing (missing-final-newline)
W:  6, 0: Redefining built-in 'pow' (redefined-builtin)
W:  6, 0: Wildcard import math (wildcard-import)
C: 11, 0: Constant name "some_global_var" doesn't conform to UPPER_CASE naming style (invalid-name)
C: 13, 0: Argument name "x" doesn't conform to snake_case naming style (invalid-name)
C: 13, 0: Argument name "y" doesn't conform to snake_case naming style (invalid-name)
C: 13, 0: Missing function docstring (missing-docstring)
W: 14, 4: Redefining name 'some_global_var' from outer scope (line 11) (redefined-outer-name)
W: 17, 4: Unreachable code (unreachable)
W: 14, 4: Unused variable 'some_global_var' (unused-variable)
...
R: 24,12: Unnecessary "else" after "return" (no-else-return)
R: 20, 0: Either all return statements in a function should return an expression, or none of them should. (inconsistent-return-statements)
C: 31, 0: Missing class docstring (missing-docstring)
W: 37, 8: Redefining name 'time' from outer scope (line 9) (redefined-outer-name)
E: 37,15: Using variable 'time' before assignment (used-before-assignment)
W: 33,50: Unused argument 'verbose' (unused-argument)
W: 36, 8: Unused variable 'list_comprehension' (unused-variable)
W: 39, 8: Unused variable 'date_and_time' (unused-variable)
R: 31, 0: Too few public methods (0/2) (too-few-public-methods)
W:  5, 0: Unused import io (unused-import)
W:  6, 0: Unused import acos from wildcard import (unused-wildcard-import)
...
W:  9, 0: Unused time imported from time (unused-import)

请注意,Pylint在每个问题区域的前缀都加上了类别 :RCWEF

  • [R]违反“良好做法”指标的因素
  • [C]违反编码标准的行为
  • [W]寻找风格问题或小编程问题
  • [E]重要的编程问题(即很可能是错误)
  • [F]防止进一步处理的错误

上面的列表直接来自 Pylint 的用户指南。

PyFlakes

PPyflakes不会告诉你丢失的文档字符串或参数名称不符合命名风格。它专注于逻辑代码问题和潜在的错误。

这里的好处是速度。PyFlakes相比Pylint已经很快。

对上面的充满lint的代码运行后的输出:

code_with_lint.py:5: 'io' imported but unused
code_with_lint.py:6: 'from math import *' used; unable to detect undefined names
code_with_lint.py:14: local variable 'some_global_var' is assigned to but never used
code_with_lint.py:36: 'pi' may be undefined, or defined from star imports: math
code_with_lint.py:36: local variable 'list_comprehension' is assigned to but never used
code_with_lint.py:37: local variable 'time' (defined in enclosing scope on line 9) referenced before assignment
code_with_lint.py:37: local variable 'time' is assigned to but never used
code_with_lint.py:39: local variable 'date_and_time' is assigned to but never used

这里的缺点是解析各种问题和错误未按类型标记或组织。

pycodestyle(以前称为PEP8)

用于检查 PEP8 中的一些样式约定。不检查命名约定,也不检查文档字符串。它捕获的错误和警告在此表中分类。

针对上面填充的 lint 填充代码运行后的输出:

code_with_lint.py:13:1: E302 expected 2 blank lines, found 1
code_with_lint.py:15:15: E225 missing whitespace around operator
code_with_lint.py:20:1: E302 expected 2 blank lines, found 1
code_with_lint.py:21:10: E711 comparison to None should be 'if cond is not None:'
code_with_lint.py:23:25: E703 statement ends with a semicolon
code_with_lint.py:27:24: E201 whitespace after '('
code_with_lint.py:31:1: E302 expected 2 blank lines, found 1
code_with_lint.py:33:58: E251 unexpected spaces around keyword / parameter equals
code_with_lint.py:33:60: E251 unexpected spaces around keyword / parameter equals
code_with_lint.py:34:28: E221 multiple spaces before operator
code_with_lint.py:34:31: E222 multiple spaces after operator
code_with_lint.py:35:22: E221 multiple spaces before operator
code_with_lint.py:35:31: E222 multiple spaces after operator
code_with_lint.py:36:80: E501 line too long (83 > 79 characters)
code_with_lint.py:40:15: W292 no newline at end of file

这个输出的好处是 lint 是按类别标记的。如果您也不关心遵守特定约定,则可以选择忽略某些错误。

Pydocstyle(原PEP257)

与 pycodestyle 非常相似,只是它不是根据 PEP8 代码样式约定进行检查,而是根据 PEP257 的约定检查文档字符串。

针对上面填充的 lint 填充代码运行后的输出:

code_with_lint.py:1 at module level:
        D200: One-line docstring should fit on one line with quotes (found 3)
code_with_lint.py:1 at module level:
        D400: First line should end with a period (not '!')
code_with_lint.py:13 in public function `multiply`:
        D103: Missing docstring in public function
code_with_lint.py:20 in public function `is_sum_lucky`:
        D103: Missing docstring in public function
code_with_lint.py:31 in public class `SomeClass`:
        D101: Missing docstring in public class
code_with_lint.py:33 in public method `__init__`:
        D107: Missing docstring in __init__

同样,像 pycodestyle 一样,pydocstyle 对它发现的各种错误进行标签和分类。而且这个列表与 pycodestyle 的任何内容都不冲突,因为所有的错误都以 D 为前缀,代表 docstring。

何时可以检查我的代码质量?

您可以检查代码的质量:

  • 编写时自动检查
  • commit时进行检查
  • 运行测试时执行检查

让 linter 经常针对您的代码运行很有用。如果没有自动化和一致性,大型团队或项目很容易忽视目标并开始创建低质量的代码。当然,它发生得很慢。一些写得不好的逻辑,或者可能是一些格式与相邻代码不匹配的代码。随着时间的流逝,所有的问题都会堆积起来。最终,您可能会遇到一些有缺陷、难以阅读、难以修复且维护痛苦的东西。

为避免这种情况,请经常检查代码质量!

当你写代码时

您可以在编写代码时使用 linter,但配置环境以执行此操作可能需要一些额外的工作。这通常是为您的 IDE 或所选编辑器找到插件的问题。事实上,大多数 IDE 已经内置了 linter。

commit代码之前

如果您使用的是 Git,则可以将 Git 钩子设置为在提交之前运行您的 linter。其他版本控制系统具有类似的方法,可以在系统中的某些操作之前或之后运行脚本。可以使用这些方法来阻止任何不符合质量标准的新代码。

强制每一部分代码都经过Linter筛选是确保持续质量的重要一步。在代码的前门自动筛选可能是避免Linter填充代码的最佳方法。

运行测试时

您还可以将linters直接放置在可用于持续集成的任何系统中。如果代码不符合质量标准,可以将 linter 设置为使生成失败。

如果现有代码中已经存在大量 linter 错误那么CI可能失效,修复历史问题成本很高。为了解决这个问题,一些持续集成系统将允许您选择仅在新代码增加已存在的 linter 错误数量时才使构建失败。这样,您就可以开始提高质量,而无需对现有代码库进行完全重写。

结论

高质量的代码可以在不中断的情况下完成它应该做的事情。它易于阅读、维护和扩展。它的功能没有问题或缺陷,并且编写时便于下一个人使用。

有一些成熟的方法和工具可以帮助提高代码质量。

风格指南将为您的代码带来一致性。PEP8是Python的一个很好的起点。Linter将帮助您识别问题区域和不一致之处。您可以在整个开发过程中使用 linter,甚至可以自动执行它们以在 lint 填充代码走得太远之前对其进行标记。

让 linters 约束风格也避免了在代码审查期间进行风格讨论的需要。有些人可能会发现从这些工具而不是团队成员那里获得坦率的反馈更容易。此外,一些团队成员可能不想在代码审查期间“吹毛求疵”风格。Linter避免政治,节省时间,并抱怨任何不一致之处。

此外,本文中提到的所有 linter 都有各种命令行选项和配置,可让您根据自己的喜好定制工具。你可以随心所欲地严格或松散,这是一件需要意识到的重要事情。

提高代码质量是一个过程。您可以采取措施改进它,而无需完全禁止所有不符合标准的代码。

相关推荐

Nat. Synthesis: 重大突破,电化学形成C-S键

第一作者:JunnanLi,HasanAl-Mahayni通讯作者:AliSeifitokaldani,NikolayKornienko通讯单位:蒙特利尔大学,麦吉尔大学【研究亮点】形成C-...

网络安全与应用(二)(网络安全理论与应用)

1、应用层安全协议SHTTP和HTTPS:SHTTP:SecHTTP,安全超文本传输协议,是HTTP扩展,使用TCP的80端口。HTTPS:HTTP+SSL,使用TCP的443端口。大部分web应用...

TN-C、TN-S、TT、IT供电系统详解及对比

TN-C、TN-S、TT、IT供电系统是低压配电系统中常见的四种接地方式,它们各自有不同的特点和适用场景。一、系统介绍TN-C供电系统①定义:整个系统中,工作零线(N线)与保护零线(PE线)是合一的,...

网络应用服务器(三)(网络应用程序服务器)

#头条创作挑战赛#1、DNS协议:域名解析协议,用于把主机域名解析为对应的IP地址。是一个分布式数据库,C/S工作方式。主要基于UDP协议,少数使用TCP,端口号都是53。常用域名如下2、DNS协议...

腾讯发布混元Turbo S:业界首次无损应用Mamba架构

21世纪经济报道记者白杨北京报道2月27日,腾讯正式发布新一代基座模型——混元TurboS。据腾讯混元团队介绍,混元TurboS在架构方面创新性地采用了Hybrid-Mamba-Transfor...

【收藏】低压配电系统中TT IT TN-S/TN-C/TN-C-S 的区别?

低压配电系统的接地型式选择是电气安全设计的核心环节,TT、IT、TN-S、TN-C、TN-C-S这五种主要接地型式因其结构、保护原理和故障特性的显著差异,在工程应用中有不同的适用范围和限制条件。如若发...

金万维公有云平台如何实现C/S架构软件快速SaaS化

金万维作为国内领先的企业信息化垂直B2B平台运营商,拥有超过5000家管理软件合作伙伴,掌握管理软件一线的发展动态,因此深知传统管理软件近年来面对的困境和问题。而SaaS却在软件行业内发展迅猛势如燎原...

随时随地做翻译:B/S架构的传奇时代到来

随着新兴技术的发展和大数据时代的到来,翻译作为连接各国语言和文化的工具,更是具有前所未有的拓展空间。传统的在计算机辅助翻译软件(CAT)上进行翻译的模式,受到时间和空间的限制,导致翻译过程中面临层层障...

BS和CS 架构的介绍(一篇就够了)(cs和bs架构的含义)

简介C/S又称Client/Server或客户/服务器模式。服务器通常采用高性能的PC、工作站或小型机,并采用大型数据库系统,如Oracle、Sybase、Informix或SQLServer。...

物管王(包租婆)软件架构与B/S和C/S架构的优点和缺点比较

一、B/S系统架构的优点和缺点优点:1)客户端无需安装,有Web浏览器即可。2)BS架构可以直接放在广域网上,通过一定的权限控制实现多客户访问的目的,交互性较强。3)BS架构无需升级多个客户端,升级服...

监听器入门看这篇就够了(怎么检查车上有没有被别人安装监听器)

什么是监听器监听器就是一个实现特定接口的普通java程序,这个程序专门用于监听另一个java对象的方法调用或属性改变,当被监听对象发生上述事件后,监听器某个方法将立即被执行。。为什么我们要使用监听器?...

购物车【JavaWeb项目、简单版】(java购物车的实现原理)

①构建开发环境免费学习资料获取方式导入需要用到的开发包建立程序开发包②设计实体书籍实体publicclassBook{privateStringid;privat...

基础篇-SpringBoot监听器Listener的使用

1.监听器Listener简介1.1监听器Listener介绍Listener是JavaWeb的三大组件(Servlet、Filter、Listener)之一,JavaWeb中的监听器主要用...

你在 Spring Boot3 整合 JWT 实现 RESTful 接口鉴权时是否遇到难题?

各位后端开发小伙伴们!在日常使用SpringBoot3搭建项目时,RESTful接口的鉴权至关重要。而JWT技术,作为一种简洁且高效的鉴权方式,被广泛应用。但大家是不是在整合过程中遇到过各...

javaWeb RSA加密使用(rsa加密java代码)

加密算法在各个网站运用很平常,今天整理代码的时候看到了我们项目中运用了RSA加密,就了解了一下。先简单说一下RSA加密算法原理,RSA算法基于一个十分简单的数论事实:将两个大质数相乘十分容易,但是想要...

取消回复欢迎 发表评论: