Notice: Function _load_textdomain_just_in_time was called incorrectly. Translation loading for the wp-pagenavi domain was triggered too early. This is usually an indicator for some code in the plugin or theme running too early. Translations should be loaded at the init action or later. Please see Debugging in WordPress for more information. (This message was added in version 6.7.0.) in /var/www/blog.zhujinhui.net/wp-includes/functions.php on line 6114

Notice: 函数 _load_textdomain_just_in_time 的调用方法不正确twentyseventeen 域的翻译加载触发过早。这通常表示插件或主题中的某些代码运行过早。翻译应在 init 操作或之后加载。 请查阅调试 WordPress来获取更多信息。 (这个消息是在 6.7.0 版本添加的。) in /var/www/blog.zhujinhui.net/wp-includes/functions.php on line 6114
学习计划 – 第 4 页 – 煅魂-JeffreyChu的修炼屋

flake8,一个让你的Python代码更漂亮的库

众所周知,PEP 8规范是 Python 社区制定的一份关于代码格式和风格的优秀指南。

高质量的代码不仅提高了软件的可维护性和可读性,还减少了错误和漏洞,从而提升了软件的可靠性和安全性。

概念

Flake8 是一个广泛使用的 Python 代码质量检查工具,它结合了多个流行的静态代码分析工具,为开发人员提供了全面的代码检查功能。Flake8 的核心组件包括:

  • PyFlakes:检测代码中的错误,例如未使用的变量或未定义的名称。
  • pycodestyle (原名 PEP8):检查代码是否符合 PEP 8,即 Python 的官方编码规范。
  • McCabe:计算代码的圈复杂度,帮助开发人员识别复杂度过高的函数和方法。

为何要装

  • 保持代码的一致性和可读性
  • 发现代码中的潜在错误和问题
  • 让代码遵循 Python 编码规范(PEP 8)

安装和配置

pip安装

pip install flake8

接下来,在项目根目录创建.flake8配置文件,如:

[flake8]
max-line-length = 88
exclude = 
  .git,
  __pycache__,
  old,
  build,
  dist
ignore = E501,W503

上述ini代码配置中:

  • max-line-length = 88表示设置了代码行的最大长度限制为 88 字符。如果代码行超过这个长度,Flake8 将报告一条错误信息。这个值不同于默认的 79 字符,通常是为了与其他工具(如black格式化器)保持一致。
  • exclude这一项指定了 Flake8 在进行代码检查时要排除的目录列表。配置中的这些目录将不会被 Flake8 检查,比如例子就排除了.git__pycache__oldbuilddist这几个目录。
  • ignore = E501,W503则选项用于指定 Flake8 在进行代码检查时应忽略的错误和警告类型。这里忽略了两种特定的错误和警告

使用 Flake8 检查代码

配置之后,就可以使用flake8进行检查代码质量,

单个文件
flake8 your_script.py
整个项目
flake8 .

其实还可以这样临时指定参数运行,这里命令行参数会覆盖掉上面的.flake8文件里的配置参数

flake8 --ignore=E501,W503 your_script.py

输出解读

在执行完flake8命令后,比如用 Flake8 检查这段代码:

def my_function():
    print("Hello, World!")
print("This line is too long, it exceeds the maximum line length recommended by PEP 8.")

我们可能看到类似这样的错误或告警的输出

example.py:3:1: E305 expected 2 blank lines after class or function definition, found 1
example.py:4:80: E501 line too long (86 > 79 characters)

这些错误/告警提示,都是在指导你怎么把代码写的更加PEP 8规范化,提示也是比较明显了。更多错误可以参考这个PEP 8文档:

https://pep8.readthedocs.io/en/latest/intro.html#error-codes

忽略规则

在 Flake8 中,除了通过在.flake8配置文件中指定文件或文件夹外,它还提供了使用特定的注释来告诉 Flake8 在代码中忽略特定行的检查。这些注释称为 “Flake8 noqa comments”。

在代码中插入 # noqa 注释可以告诉 Flake8 忽略该行的检查。这在某些情况下很有用,例如当你知道某一行违反了规范,但你有充分的理由保留它时(尤其是一些比较急的热修复时)。

全局禁用
# flake8: noqa
import os  # noqa

在这个例子中,# flake8: noqa 告诉 Flake8 忽略整个文件的检查,而 # noqa 注释告诉 Flake8 忽略 import os 这一行的检查。

特定禁用
import os  # noqa: F401

在这个例子中,# noqa: F401 告诉 Flake8 忽略 import os 这一行的 F401 错误(未使用的导入)检查。

注意事项

尽量减少使用 # noqa 注释,它们应该只在确实需要的情况下使用,并且要确保有充分的理由来禁用检查,否则会导致漏检查引发隐藏问题。

集成到工作流

  • 在 IDE/编辑器中集成 Flake8(如 VSCode、PyCharm),这样在写代码的时候就能注意到问题
  • 在 CI/CD 管道中使用 Flake8, 比如在 GitHub Actions 中使用 Flake8
name: Flake8 Lint

on: [push, pull_request]

jobs:
  flake8:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Set up Python
        uses: actions/setup-python@v2
        with:
          python-version: '3.x'
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install flake8
      - name: Run Flake8
        run: |
          flake8 .

这样就能保证合并到仓库的代码是统一规范的。

插件

Flake8 还提供了其他优秀插件扩展功能,比如flake8-docstrings可以帮我们进行一些文档字符串检查(如函数/类/模块是否有文档注释等)

总结

Flake8 是 Python 开发中常用的代码质量检查工具,通过集成多个静态分析工具,帮助开发者确保代码符合PEP 8规范,提高可读性和一致性,提高了团队开发的效率。

参考链接

  • flake8 官方文档:https://flake8.pycqa.org/en/latest/index.html#quickstart
  • PEP 8 编码规范:https://wangmeng-python.readthedocs.io/en/latest/readability/PEP8.html

Python面试必问5:什么是GIL?

GIL是Python中一个非常重要的概念,几乎所有的python开发面试都会问到这个问题,今天我们来细细盘点下。

概念(重点)

GIL(Global Interpreter Lock,全局解释器锁) 是Python解释器(特别是CPython实现)中的一个机制。它是一种互斥锁,用于保护Python解释器内部的全局状态,确保同时只有一个线程执行Python字节码。换句话说,GIL限制了多线程程序中多个线程的并行(注意不是并发)执行。

GIL产生原因

  • GIL简化了Python解释器的内存管理保护(多)线程安全。Python使用引用计数机制来管理内存,GIL确保了引用计数操作的原子性,避免了多线程环境下引用计数出错的问题。如果没有GIL,多线程同时操作引用计数时,可能会导致内存泄漏或崩溃
  • CPython的历史遗留问题,CPython是Python的最早实现,设计时并没有充分考虑多线程并发的支持。
  • 提高单线程性能
  • 第三方C扩展的兼容性需要进行大改才能确保线程安全。

工作原理

GIL的持有和释放过程可以分为以下几个步骤:

获取GIL
  • 当一个线程要执行Python字节码时,它首先必须获取GIL。
  • 获取GIL的过程是互斥的,即同一时刻只有一个线程能成功获取GIL。
  • 如果GIL已经被其他线程持有,当前线程将进入等待状态,直到GIL被释放。
执行Python代码
  • 一旦线程获取了GIL,它便可以开始执行Python字节码。
  • 线程执行过程中,每经过一定数量的字节码指令(如100个字节码指令),解释器会检查是否需要释放GIL,以便让其他线程有机会执行。
释放GIL
  • 当线程完成了一定数量的字节码指令或进入I/O(如文件读取、网络请求等)操作时,它会释放GIL。
  • 释放GIL后,其他等待的线程可以尝试获取GIL并开始执行。
再度获取GIL

如果一个线程在释放GIL后仍需继续执行,它必须重新尝试获取GIL。这意味着,即使是同一个线程,也无法保证连续长时间持有GIL。
以下是一个简化的示例,描述了GIL的获取和释放:

import threading

def thread_function():
    while True:
        # 尝试获取GIL
        with gil:
            # 执行Python字节码
            execute_bytecode()
            # 每执行一定数量的字节码指令后,检查是否需要释放GIL
            if bytecode_count >= threshold:
                bytecode_count = 0
                # 释放GIL
                release_gil()

# 创建多个线程
threads = [threading.Thread(target=thread_function) for _ in range(10)]
for thread in threads:
    thread.start()

for thread in threads:
    thread.join()

在这个简化的示例中,线程在执行过程中会周期性地释放GIL,让其他线程有机会执行。这种机制虽然限制了多线程的并行性,但也确保了Python解释器的稳定和内存管理的简单性。

影响

CPU密集型任务

GIL限制了多线程程序的并行执行,使得CPU密集型任务无法充分利用多核CPU的计算能力,导致性能提升有限甚至下降。

I/O密集型任务

在I/O密集型任务中,由于I/O操作会释放GIL,其他线程可以在等待I/O操作时执行,从而实现较好的并发性,受GIL的影响较小。因此,多线程在I/O密集型任务中仍然能够提供显著的性能提升。

如何绕过GIL

多进程方案

使用多进程(multiprocessing模块)绕过GIL:

  • 原理:多进程通过创建多个独立的进程来绕过GIL,每个进程都有自己的Python解释器和内存空间,因此它们不会争夺GIL,可以实现真正的并行执行。
  • 实现方法: Python的multiprocessing模块提供了方便的接口来创建和管理多个进程。
  • 优点:可以充分利用多核CPU,真正并行执行,提升性能。
  • 缺点:进程间通信开销较大,内存使用量较多。
原生线程库

使用C扩展或其他语言(如Cython)绕过GIL:

  • 原理:通过使用C语言编写Python扩展模块或使用Cython编写部分代码,可以在扩展模块中释放GIL,让计算密集型任务并行执行。
  • 实现方法:
  • C扩展: 在C代码中使用Python提供的API(如Py_BEGIN_ALLOW_THREADS和Py_END_ALLOW_THREADS)释放和重新获取GIL。
  • Cython: 在Cython代码中,通过声明nogil块来释放GIL。
  • 优点:可以在特定的代码段中实现并行计算,提升性能。
  • 缺点:需要额外的开发工作和学习成本,复杂度增加。
异步编程

Python中的异步编程(asyncio):

  • 原理:异步编程通过事件循环和协程的方式处理并发任务,适用于I/O密集型任务。与多线程不同,异步编程不需要创建多个线程,而是通过单线程处理多个I/O操作,避免了GIL的限制。
  • 实现方法:使用asyncio模块,通过async和await关键字定义和执行异步任务。
  • 优点:高效处理I/O密集型任务,资源占用少,代码相对简洁。
  • 缺点:不适用于CPU密集型任务,需要对异步编程模式有一定的理解。

未来

Python社区一直在探索替代GIL的方案,以提升多线程性能。未来,可能会引入细粒度锁或其他并发模型,但这需要确保向后兼容和性能稳定。PyPy等替代解释器也在积极尝试绕过GIL的限制。

不过目前看来,任重而道远啊!

总结

GIL是Python解释器中的全局锁,限制了多线程并行执行,尤其影响CPU密集型任务,但对I/O密集型任务影响较小。尽管它简化了内存管理,提升了单线程性能,但限制了多核利用。多进程、C扩展和异步编程是绕过GIL的有效方案。