当前位置:首页>python>Python 代码美容师:Black 完全使用指南

Python 代码美容师:Black 完全使用指南

  • 2026-03-23 20:08:41
Python 代码美容师:Black 完全使用指南

让每一行 Python 代码都散发优雅的气息

第一部分:基础入门


第1章:Black 概述

1.1 什么是代码格式化工具

在软件开发中,代码格式化工具是一种自动调整源代码布局的工具,使其遵循特定的风格规范。这些工具处理的是代码的视觉呈现,而不是其逻辑功能。

传统手写代码的问题:

# 不同开发者可能写出完全不同的格式defcalculate(x,y,z):return x+y*z # 紧凑风格defcalculate(x, y, z):return x + y * z  # 标准风格defcalculate(    x,    y,    z):return x + y * z  # 垂直风格

代码格式化工具的作用:

  • 消除风格争论,让团队专注于代码逻辑
  • 自动修复常见的格式问题
  • 保持整个代码库的一致性
  • 减少代码审查中关于格式的评论

1.2 Black 的设计哲学

Black 的核心哲学是 **"不妥协"**(No Compromises)。这个名字来源于亨利·福特的著名言论:"你可以拥有任何你喜欢的颜色,只要是黑色。"("You can have any color you want, as long as it's black.")

核心设计原则:

  1. 最小化可配置性

    • 大多数格式化工具提供大量配置选项,导致"配置疲劳"
    • Black 将配置选项降到最低,只有几个关键参数可调整
  2. 一致性优先于个人偏好

    # 无论谁运行 Black,输出都完全一致# 输入(任何风格)my_list=[1,2,3,4]# 输出(统一风格)my_list = [1234]
  3. 自动且无痛

    • 无需手动调整格式
    • 保存文件时自动格式化
    • 与版本控制系统无缝集成

Black 的设计目标:

  • 让代码格式化变得无聊(自动化完成)
  • 节省开发者的时间和精力
  • 消除代码审查中的格式争议
  • 生成可读性强、一致的代码

1.3 Black 与其他格式化工具对比

对比表格

特性
Black
autopep8
yapf
isort
配置灵活性
极低
中等
中等
PEP 8 遵循度
严格
严格
可配置
专注于导入
速度
中等
自动修复
完全自动
部分自动
完全自动
完全自动
学习曲线
中等
社区采用率
中等
中等

详细对比

Black vs autopep8:

# autopep8 主要修复 PEP 8 违规,但保留原有结构# 输入deffoo():return42# autopep8 输出deffoo():return42# Black 输出(可能重新格式化更多内容)deffoo():return42

Black vs yapf:

# yapf 允许高度自定义,Black 风格固定# 相同的输入,不同的 yapf 配置会产生不同的输出# yapf (google 风格)defvery_long_function_name(argument1, argument2, argument3):return argument1 + argument2 + argument3# yapf (pep8 风格)defvery_long_function_name(        argument1, argument2, argument3):return argument1 + argument2 + argument3# Black 风格(唯一)defvery_long_function_name(    argument1, argument2, argument3):return argument1 + argument2 + argument3

Black vs isort:

  • isort 专注于导入语句的排序和分组
  • Black 处理所有代码,但导入部分可以与 isort 配合使用

1.4 Black 的优势与局限性

优势

  1. 零配置体验

    • 开箱即用,无需纠结于格式配置
    • 团队间完全一致的代码风格
  2. 提高生产力

    # 不再需要手动调整格式# 保存即格式化,节省大量时间
  3. 减少代码审查负担

    • 审查时只关注逻辑问题
    • 消除"这里少了个空格"之类的评论
  4. 强大的社区支持

    • 被主流编辑器集成
    • 与 CI/CD 工具无缝对接
    • 活跃的维护和更新
  5. 确定性输出

    • 相同输入始终产生相同输出
    • 便于调试和版本控制

局限性

  1. 缺乏灵活性

    # 某些团队可能有特殊的风格要求# Black 不会妥协,必须接受其风格
  2. 有时产生不理想的格式

    # Black 可能会将代码格式化为# 你认为不如原始版本的方式result = some_function(    argument1, argument2, argument3, argument4, argument5)# 而不是你可能想要的更紧凑的形式
  3. 处理复杂代码时的挑战

    • 某些复杂的表达式可能被格式化得不够理想
    • 对装饰器的处理可能不符合某些团队的偏好
  4. 迁移成本

    • 大型项目迁移到 Black 会产生大量的代码变更
    • 可能与其他工具(如 flake8)的某些规则冲突
  5. 学习曲线

    • 虽然使用简单,但需要理解其工作方式和配置选项
    • 需要团队成员达成共识采用

第2章:安装与环境配置

2.1 安装方法

pip 安装(最简单)

# 基本安装pip install black# 安装特定版本pip install black==23.7.0# 升级到最新版本pip install --upgrade black

pipx 安装

pipx 专门用于安装 Python 命令行工具,将其隔离在独立环境中:

# 安装 pipxpip install pipxpipx ensurepath# 使用 pipx 安装 blackpipx install black# 验证安装black --version

pipx 的优势:

  • 隔离环境,避免依赖冲突
  • 全局可用,不污染项目环境
  • 便于版本管理

包管理器安装

macOS (Homebrew):

brew install black

Ubuntu/Debian:

sudo apt updatesudo apt install black

Conda (跨平台):

conda install -c conda-forge black

项目级安装(推荐)

在项目中使用 requirements.txt 或 pyproject.toml

# requirements.txtblack==23.7.0

或使用 Poetry:

poetry add --dev black

或使用 pipenv:

pipenv install --dev black

2.2 验证安装

# 检查版本black --version# 输出示例: black, version 23.7.0# 查看帮助black --help# 快速测试echo"print('hello')" | black -  # 使用 stdin

2.3 编辑器集成

VS Code 配置

方法 1:使用 Python 扩展

  1. 安装 Python 扩展(Microsoft)
  2. 设置 settings.json
{// 设置 Black 为默认格式化工具"[python]": {"editor.defaultFormatter""ms-python.black-formatter","editor.formatOnSave"true    },// 可选:自定义 Black 参数"black-formatter.args": ["--line-length=100"    ],// 如果使用 black 直接"python.formatting.provider""black","python.formatting.blackArgs": ["--line-length=100"],"editor.formatOnSave"true}

方法 2:使用 Black 扩展

  1. 安装 "Black Formatter" 扩展
  2. 配置保存时自动格式化:
{"editor.formatOnSave"true,"[python]": {"editor.defaultFormatter""ms-python.black-formatter"    }}

PyCharm/IntelliJ IDEA 配置

  1. 安装 Black:

    pip install black
  2. 配置外部工具:

    • Name: Black
    • Program: $PyInterpreterDirectory$/black 或直接 black
    • Arguments: $FilePath$
    • Working directory: $ProjectFileDir$
    • File → Settings → Tools → External Tools
    • 点击 "+" 添加新工具
    • 填写信息:
  3. 设置快捷键:

    • Settings → Keymap
    • 搜索 "External Tools"
    • 为 Black 设置快捷键(如 Ctrl+Alt+L)
  4. 保存时自动格式化(可选):

    • Settings → Tools → Actions on Save
    • 勾选 "Run External Tools"
    • 选择 Black

Vim/Neovim 配置

使用 vim-black 插件:

" 使用 vim-plug 安装Plug 'psf/black'" 配置自动格式化autocmd BufWritePre *.py execute ':Black'" 或设置快捷键nnoremap <C-b> :Black<CR>

使用 ALE 插件:

" 安装 ALEPlug 'dense-analysis/ale'" 配置 ALE 使用 blacklet g:ale_python_black_executable = 'black'let g:ale_python_black_options = '--line-length 100'let g:ale_fixers = {\   'python': ['black'],\}

原生 LSP 配置(Neovim 0.5+):

-- 使用 null-ls 配置local null_ls = require("null-ls")null_ls.setup({    sources = {        null_ls.builtins.formatting.black,    },})-- 保存时自动格式化vim.api.nvim_create_autocmd("BufWritePre", {    pattern = "*.py",    callback = function()        vim.lsp.buf.format({ async = false })end,})

Sublime Text 配置

  1. 安装 Package Control

  2. 安装 Black 插件:

    • Ctrl+Shift+P → "Install Package"
    • 搜索 "Black" 并安装
  3. 配置快捷键:

    // Preferences → Key Bindings{"keys": ["ctrl+alt+b"],"command""black"}
  4. 保存时自动格式化:

    // 在用户设置中添加{"black_on_save"true}

第3章:基本使用

3.1 命令行基础用法

基本命令

# 格式化单个文件black my_script.py# 格式化多个文件black file1.py file2.py file3.py# 格式化整个目录(递归)black src/black .  # 当前目录# 检查文件是否需要格式化(不修改)black --check my_script.py# 如果文件已经格式化,退出码为0# 如果需要格式化,退出码为1并输出未格式化的文件列表# 显示需要修改的差异(不修改)black --diff my_script.py# 详细输出black --verbose my_script.py# 静默模式(只输出错误)black --quiet my_script.py

实际示例

示例 1:格式化前

# messy_code.pydefadd_numbers(a,b,c):return a+b+cresult=add_numbers(1,2,3)print(f"The result is {result}")classMyClass:pass

运行 Black:

black messy_code.py

格式化后:

# messy_code.py (格式化后)defadd_numbers(a, b, c):return a + b + cresult = add_numbers(123)print(f"The result is {result}")classMyClass:pass

3.2 常用参数详解

--line-length (行长度)

# 默认 88 个字符black script.py# 自定义行长度black --line-length 100 script.py

效果对比:

# 默认 88 字符defvery_long_function_name_with_many_parameters(    parameter_one, parameter_two, parameter_three, parameter_four):pass# 120 字符(更宽松)defvery_long_function_name_with_many_parameters(parameter_one, parameter_two, parameter_three, parameter_four):pass

--target-version (Python 目标版本)

# 指定 Python 版本black --target-version py310 script.pyblack --target-version py39 script.py# 支持多个版本black --target-version py39 --target-version py310 script.py

支持的版本:

  • py37py38py39py310py311py312

作用: Black 会根据目标版本决定是否使用某些语法特性。

--skip-string-normalization (字符串规范化)

# 默认:规范字符串引号(使用双引号)black script.py# 跳过字符串规范化,保留原始引号black --skip-string-normalization script.py

效果对比:

# 输入name = 'John'message = "Hello"# 默认输出(规范化为双引号)name = "John"message = "Hello"# 跳过规范化(保留原始引号)name = 'John'message = "Hello"

--fast vs --safe

# 安全模式(默认)- 运行所有安全检查black --safe script.py# 快速模式 - 跳过安全检查,速度更快black --fast script.py

注意:--fast 模式可能会生成无法运行的代码,谨慎使用。

--include 和 --exclude

# 只格式化匹配特定模式的文件black --include '\.pyi?$' src/# 排除特定目录black --exclude '/(\.venv|build|dist)/' .

3.3 实战练习:格式化你的第一个项目

练习 1:创建混乱的代码

创建文件 calculator.py

# calculator.py - 故意格式混乱的代码import math, sysfrom typing import List,UnionclassCalculator:defadd(self,a,b):return a+bdefsubtract(self,a,b):return a-bdefmultiply(self,a,b):return a*bdefdivide(self,a,b):if b==0:raise ValueError("Cannot divide by zero")return a/bdefpower(self,base,exponent):return math.pow(base,exponent)defsqrt(self,x):if x<0:raise ValueError("Cannot take sqrt of negative")return math.sqrt(x)defcalculate_average(numbers:List[Union[int,float]])->float:ifnot numbers:return0.0return sum(numbers)/len(numbers)defmain():    calc=Calculator()    print(calc.add(5,3))    print(calc.subtract(10,4))    print(calc.multiply(6,7))    print(calc.divide(15,3))    print(calc.power(2,8))    print(calc.sqrt(16))    avg=calculate_average([1,2,3,4,5])    print(f"Average: {avg}")if __name__=="__main__":main()

练习 2:格式化文件

# 1. 先检查是否需要格式化black --check calculator.py# 2. 查看将要做的更改black --diff calculator.py# 3. 实际格式化black calculator.py# 4. 再次检查确认black --check calculator.py

练习 3:自定义配置

# 使用自定义行长度black --line-length 120 calculator.py# 跳过字符串规范化black --skip-string-normalization calculator.py# 组合多个选项black --line-length 100 --target-version py310 calculator.py

练习 4:批量格式化

# 创建测试目录结构mkdir -p myproject/{src,tests}touch myproject/src/{module1,module2,utils}.pytouch myproject/tests/{test_module1,test_module2}.py# 格式化整个项目black myproject/# 只格式化源代码目录black myproject/src/# 排除测试目录black myproject/ --exclude '/tests/'

练习 5:集成到日常开发

创建 .git/hooks/pre-commit 脚本:

#!/bin/bash# 简单的 Git 预提交钩子# 获取所有将要提交的 Python 文件FILES=$(git diff --cached --name-only --diff-filter=ACM | grep '\.py$')if [ -n "$FILES" ]; thenecho"Running Black on Python files..."    black $FILES# 重新添加格式化后的文件    git add $FILESecho"Black formatting applied."fi

使脚本可执行:

chmod +x .git/hooks/pre-commit

练习 6:使用 Black 作为 Python 模块

# format_script.pyimport blackimport sysdefformat_code(source_code):"""使用 Black 格式化代码字符串"""try:        mode = black.Mode(line_length=88)        formatted = black.format_str(source_code, mode=mode)return formattedexcept Exception as e:        print(f"格式化错误: {e}")return source_code# 从文件读取并格式化defformat_file(filename):with open(filename, 'r', encoding='utf-8'as f:        content = f.read()    formatted = format_code(content)with open(filename, 'w', encoding='utf-8'as f:        f.write(formatted)    print(f"已格式化: {filename}")if __name__ == "__main__":if len(sys.argv) > 1:        format_file(sys.argv[1])else:        print("请提供文件名")

练习 7:创建别名简化命令

Linux/macOS (.bashrc 或 .zshrc):

alias bf='black --line-length 100'alias bfc='black --check'alias bfd='black --diff'

Windows (PowerShell profile):

function bf { black --line-length 100 $args }function bfc { black --check $args }function bfd { black --diff $args }

练习总结

完成这些练习后,你应该能够:

  • 格式化单个文件和整个项目
  • 使用各种命令行选项
  • 集成 Black 到版本控制
  • 在 Python 代码中使用 Black
  • 创建自定义的工作流

第二部分:中级应用

第4章:配置与自定义

4.1 配置文件(pyproject.toml)

Black 使用 pyproject.toml 作为配置文件,这是 PEP 518 和 PEP 621 定义的标准 Python 项目配置文件。

完整的配置文件示例

# pyproject.toml[tool.black]# 基础配置line-length = 88target-version = ['py310', 'py311']include = '\.pyi?$'extend-exclude = '''/(    # 排除的目录    \.eggs  | \.git  | \.hg  | \.mypy_cache  | \.tox  | \.venv  | _build  | buck-out  | build  | dist  | \.pytest_cache  | \.vscode  | __pycache__)/'''# 字符串规范化(可选)skip-string-normalization = false# 魔法逗号处理(可选)skip-magic-trailing-comma = false# 预览模式(使用新特性)preview = false# 详细输出verbose = false

配置文件详解

line-length (默认: 88)

[tool.black]line-length = 100  # 更宽松的行长度

target-version (默认: 当前 Python 版本)

[tool.black]target-version = ['py37', 'py38', 'py39', 'py310']

include 和 exclude

[tool.black]# 包含的模式(正则表达式)include = '\.pyi?$'  # 匹配 .py 和 .pyi 文件# 排除的模式exclude = '''/(    \.eggs  | \.git  | \.venv  | build  | dist)/'''

skip-string-normalization (默认: false)

[tool.black]skip-string-normalization = true  # 保留原始引号

skip-magic-trailing-comma (默认: false)

[tool.black]skip-magic-trailing-comma = true  # 禁用魔法逗号特性

preview (默认: false)

[tool.black]preview = true  # 启用预览模式的新特性

required-version (指定所需版本)

[tool.black]required-version = "23.7.0"  # 如果版本不匹配会警告

多项目配置

对于 monorepo,可以在不同子目录使用不同的配置:

# 项目根目录 pyproject.toml[tool.black]line-length = 88target-version = ['py310']# 子目录 app/legacy/pyproject.toml(覆盖配置)[tool.black]line-length = 100target-version = ['py37']  # 旧代码使用旧版本

4.2 命令行参数与配置文件的优先级

Black 的配置优先级(从高到低):

  1. 命令行参数
  2. 项目目录的 pyproject.toml
  3. 父目录的 pyproject.toml
  4. 用户配置(未来支持)
  5. 默认值

优先级示例

# pyproject.toml 中设置 line-length = 88# 命令行参数会覆盖black --line-length 120 script.py  # 使用 120# 多个命令行参数black --line-length 100 --skip-string-normalization script.py

验证配置

# 查看当前使用的配置black --verbose script.py# 输出示例:# Using configuration from /path/to/pyproject.toml.# line-length: 88# target-versions: ['py310', 'py311']# skip-string-normalization: False

4.3 排除文件和目录

排除模式详解

[tool.black]# 简单排除(逗号分隔)exclude = '''\.venv| build| dist'''# 复杂排除(使用正则)exclude = '''/(    # 排除虚拟环境    \.venv  | \.env  | venv  | env  | \.pytest_cache  | \.mypy_cache  | \.tox  | \.eggs  | \.git  | \.hg  | \.svn  | __pycache__  # 排除构建目录  | build  | dist  | wheels  # 排除第三方代码  | site-packages  | node_modules  # 排除特定文件  | migrations  | conftest\.py  | setup\.py)/'''

使用 .gitignore 兼容

Black 默认会读取 .gitignore 来排除文件。可以通过配置覆盖:

[tool.black]# 忽略 .gitignore(不推荐)force-exclude = '''/(    \.venv)/'''

实际应用场景

场景 1:排除迁移文件

[tool.black]exclude = '''/migrations/'''

场景 2:排除生成的文件

[tool.black]exclude = '''/generated/| \.pb2\.py$      # protobuf 生成的文件| \_pb2\.py$'''

场景 3:排除测试文件中的特定模式

[tool.black]exclude = '''/tests/fixtures/| /tests/legacy/'''

4.4 处理魔法逗号

魔法逗号(magic trailing comma)是 Black 的一个特性,用于控制容器格式。

什么是魔法逗号

# 没有魔法逗号 - 单行显示my_list = [123]# 有魔法逗号 - 每行一个元素my_list = [1,2,3,]# 魔法逗号触发垂直格式function_call(    arg1,    arg2,    arg3,)

魔法逗号规则

规则 1:容器内的魔法逗号

# 输入data = [1,2,3# 没有尾随逗号]# Black 输出(单行)data = [123]# 输入(有尾随逗号)data = [1,2,3,  # 有尾随逗号]# Black 输出(垂直格式)data = [1,2,3,]

规则 2:函数参数的魔法逗号

# 输入(无尾随逗号)deflong_function_name(    param1,    param2,    param3):pass# Black 输出(如果行长度允许)deflong_function_name(param1, param2, param3):pass# 输入(有尾随逗号)deflong_function_name(    param1,    param2,    param3,):pass# Black 输出(强制垂直格式)deflong_function_name(    param1,    param2,    param3,):pass

规则 3:导入语句的魔法逗号

# 无尾随逗号from module import (func1, func2, func3)# 有尾随逗号(强制垂直)from module import (    func1,    func2,    func3,)

禁用魔法逗号

[tool.black]skip-magic-trailing-comma = true

禁用后,即使有尾随逗号也不会触发垂直格式:

# skip-magic-trailing-comma = truedata = [1,2,3,  # 仍然会被压缩]# 输出:data = [1, 2, 3]

魔法逗号最佳实践

  1. 使用尾随逗号控制格式

    # 当需要垂直显示时,添加尾随逗号config = {'key1''value1','key2''value2',  # 尾随逗号保持垂直格式}
  2. Git diff 友好

    # 有尾随逗号时,添加新元素只改变一行items = [1,2,3,  # 尾随逗号]# 添加新元素时items = [1,2,3,4,  # 只添加这一行]
  3. 避免不必要的魔法逗号

    # 不推荐:不必要的尾随逗号single = [1,]  # 会触发垂直格式,但只有一个元素# 推荐single = [1]

实践练习

创建一个文件 magic_comma_demo.py

# 测试魔法逗号效果# 示例 1:列表numbers1 = [12345]numbers2 = [1,2,3,4,5,]# 示例 2:字典config1 = {'host''localhost''port'8080'debug'True}config2 = {'host''localhost','port'8080,'debug'True,}# 示例 3:函数调用result1 = calculate(10203040)result2 = calculate(10,20,30,40,)# 示例 4:函数定义defprocess_data1(data, transform, validate):passdefprocess_data2(    data,    transform,    validate,):pass

运行 Black 观察效果:

# 默认行为black magic_comma_demo.py# 禁用魔法逗号black --skip-magic-trailing-comma magic_comma_demo.py

第5章:集成开发工作流

5.1 与 Git 集成

使用 pre-commit 框架

pre-commit 是最流行的 Git 钩子管理工具。

安装 pre-commit:

pip install pre-commit

创建 .pre-commit-config.yaml:

# .pre-commit-config.yamlrepos:# Black 代码格式化-repo:https://github.com/psf/blackrev:23.7.0# 使用最新稳定版本hooks:-id:blacklanguage_version:python3.10args:[--line-length=88]# 可选:添加其他钩子-repo:https://github.com/pycqa/isortrev:5.12.0hooks:-id:isortname:isort(python)args:["--profile","black"]-repo:https://github.com/pycqa/flake8rev:6.1.0hooks:-id:flake8args:[--max-line-length=88,--extend-ignore=E203]

安装钩子:

# 安装 pre-commit 钩子pre-commit install# 可选:在 CI 中运行所有文件pre-commit run --all-files# 手动运行特定钩子pre-commit run black --all-files

钩子行为:

# 提交时自动运行git commit -m "Add new feature"# 输出示例:# black....................................................................Failed# - hook id: black# - files were modified by this hook# reformatted script.py# All done! ✨ 🍰 ✨# 1 file reformatted.

手动配置 pre-commit 钩子

创建 .git/hooks/pre-commit

#!/bin/bash# 手动配置的 pre-commit 钩子echo"Running Black formatter..."# 获取所有暂存的 Python 文件STAGED_PYTHON_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep '\.py$')if [ -n "$STAGED_PYTHON_FILES" ]; then# 运行 Black    black $STAGED_PYTHON_FILES# 重新暂存格式化后的文件    git add $STAGED_PYTHON_FILESecho"Black formatting applied to:"echo"$STAGED_PYTHON_FILES"elseecho"No Python files to format."fiexit 0

使钩子可执行:

chmod +x .git/hooks/pre-commit

使用 Git 属性过滤

创建 .gitattributes

# 在提交时自动格式化*.py filter=black

配置 Git 过滤器:

git config --global filter.black.clean 'black --quiet -'git config --global filter.black.smudge 'cat'

5.2 IDE/编辑器保存时自动格式化

VS Code 详细配置

settings.json 完整示例:

{// Python 特定配置"[python]": {"editor.defaultFormatter""ms-python.black-formatter","editor.formatOnSave"true,"editor.codeActionsOnSave": {"source.organizeImports"true// 配合 isort        }    },// Black 格式化器配置"black-formatter.args": ["--line-length=100","--skip-string-normalization"    ],// 全局格式化设置"editor.formatOnSave"true,"editor.formatOnPaste"false,"editor.formatOnType"false,// 多语言支持"files.associations": {"*.py""python"    }}

工作区特定配置(.vscode/settings.json):

{// 项目特定配置"python.formatting.provider""black","python.formatting.blackPath""${workspaceFolder}/venv/bin/black","python.formatting.blackArgs": ["--config=${workspaceFolder}/pyproject.toml"    ],"editor.formatOnSave"true}

PyCharm 高级配置

配置 Black 作为外部工具(增强版):

  1. File → Settings → Tools → External Tools

  2. 创建 Black 工具:

    • Name:Black Formatter
    • Program:$PyInterpreterDirectory$/black
    • Arguments:$FilePath$ --line-length 100
    • Working directory:$ProjectFileDir$
    • Output filters:$FILE_PATH$\:$LINE$\:$COLUMN$\:.*
  3. 配置 File Watcher(实时格式化):

    • Name:Black
    • File type:Python
    • Scope:Project Files
    • Program:$PyInterpreterDirectory$/black
    • Arguments:$FilePath$
    • Output paths to refresh:$FilePath$
    • Working directory:$ProjectFileDir$
    • Auto-save edited files to trigger the watcher:
    • File → Settings → Tools → File Watchers
    • 添加新 File Watcher:
  4. 保存时自动格式化(Macros):

    • Edit → Macros → Start Macro Recording
    • 执行:保存文件 → 运行 Black 工具
    • Stop Macro Recording → 命名为 "Save and Format"
    • File → Settings → Keymap → 绑定宏到 Ctrl+S

Vim/Neovim 高级配置

使用 ALE 的完整配置:

" .vimrc 或 init.vim" 使用 vim-plugcall plug#begin()Plug 'dense-analysis/ale'Plug 'psf/black'call plug#end()" ALE 配置let g:ale_fixers = {\   'python': ['black', 'isort'],\}let g:ale_fix_on_save = 1let g:ale_python_black_options = '--line-length=100'let g:ale_python_isort_options = '--profile black'" 快捷键nnoremap <leader>f :call Black()<CR>nnoremap <leader>c :ALEFix<CR>" 状态栏显示let g:ale_statusline_format = ['✗ %d', '⚡ %d', '✓ OK']

Neovim Lua 配置:

-- init.lualocal null_ls = require("null-ls")null_ls.setup({    sources = {        null_ls.builtins.formatting.black.with({            extra_args = { "--line-length""100" }        }),        null_ls.builtins.formatting.isort.with({            extra_args = { "--profile""black" }        })    },    on_attach = function(client, bufnr)-- 保存时格式化        vim.api.nvim_create_autocmd("BufWritePre", {            buffer = bufnr,            callback = function()                vim.lsp.buf.format({ async = false })end        })end})

Emacs 配置

;; .emacs 或 init.el;; 使用 blacken(use-package blacken  :ensure t  :hook  (python-mode . blacken-mode)  :config  (setq blacken-line-length 100));; 保存时自动格式化(add-hook 'python-mode-hook          (lambda ()            (add-hook 'before-save-hook 'blacken-buffer nil t)))

5.3 在 CI/CD 管道中使用

GitHub Actions

基本检查工作流:

# .github/workflows/lint.ymlname:Linton:push:branches:[main,develop]pull_request:branches:[main]jobs:lint:runs-on:ubuntu-lateststrategy:matrix:python-version:["3.8","3.9","3.10","3.11"]steps:-uses:actions/checkout@v3-name:SetupPython${{matrix.python-version}}uses:actions/setup-python@v4with:python-version:${{matrix.python-version}}-name:Installdependenciesrun:|        python -m pip install --upgrade pip        pip install black-name:CheckformattingwithBlackrun:|        black --check --diff .

完整的 CI/CD 工作流:

# .github/workflows/ci.ymlname:CIPipelineon:push:branches:[main,develop]pull_request:branches:[main]jobs:format-check:runs-on:ubuntu-lateststeps:-uses:actions/checkout@v3-name:SetupPythonuses:actions/setup-python@v4with:python-version:'3.10'cache:'pip'-name:InstallBlackrun:pipinstallblack-name:Checkformattingrun:|          black --check --diff --verbose .-name:Uploaddiffasartifact(iffailed)if:failure()uses:actions/upload-artifact@v3with:name:black-diffpath:|            black_diff.txtretention-days:7auto-format:if:github.event_name=='push'&&github.ref=='refs/heads/main'runs-on:ubuntu-lateststeps:-uses:actions/checkout@v3with:token:${{secrets.GITHUB_TOKEN}}-name:SetupPythonuses:actions/setup-python@v4with:python-version:'3.10'-name:InstallBlackrun:pipinstallblack-name:Formatcoderun:black.-name:Commitchangesuses:stefanzweifel/git-auto-commit-action@v4with:commit_message:"style: apply black formatting"branch:${{github.head_ref}}

GitLab CI/CD

# .gitlab-ci.ymlstages:-lint-testvariables:PIP_CACHE_DIR:"$CI_PROJECT_DIR/.cache/pip"cache:paths:-.cache/pipbefore_script:-python-V-pipinstallvirtualenv-virtualenvvenv-sourcevenv/bin/activate-pipinstallblackblack-check:stage:lintscript:-black--check--diff.artifacts:when:on_failurepaths:-black_diff.txtexpire_in:1weekblack-format:stage:lintscript:-black.-gitdiff--exit-codeonly:-merge_requestsallow_failure:true

Jenkins Pipeline

// Jenkinsfilepipeline {    agent any    stages {        stage('Checkout') {            steps {                checkout scm            }        }        stage('Setup') {            steps {                sh '''                    python -m venv venv                    source venv/bin/activate                    pip install black                '''            }        }        stage('Black Check') {            steps {                sh '''                    source venv/bin/activate                    black --check --diff . || true                '''            }            post {                always {                    script {// 生成报告                        sh '''                            source venv/bin/activate                            black --check . > black-report.txt || true                        '''// 发布报告(可选)                        publishHTML([                            reportDir:'.',                            reportFiles:'black-report.txt',                            reportName:'Black Format Check'                        ])                    }                }            }        }        stage('Format (Optional)') {            when {                expression { env.BRANCH_NAME == 'develop' }            }            steps {                sh '''                    source venv/bin/activate                    black .                    # 提交格式化的代码                    git config user.email "ci@example.com"                    git config user.name "CI Bot"                    git commit -am "style: apply black formatting" || true                    git push origin HEAD:$BRANCH_NAME || true                '''            }        }    }    post {        always {            cleanWs()        }    }}

Azure DevOps

# azure-pipelines.ymltrigger:-main-developpool:vmImage:ubuntu-lateststeps:-task:UsePythonVersion@0inputs:versionSpec:'3.10'addToPath:true-script:|    python -m pip install --upgrade pip    pip install blackdisplayName:'Install Black'-script:|    black --check --diff .displayName:'Check formatting with Black'continueOnError:true-task:PublishBuildArtifacts@1condition:failed()inputs:PathtoPublish:'black_diff.txt'ArtifactName:'black-formatting-diff'

Travis CI

# .travis.ymllanguage:pythonpython:-"3.8"-"3.9"-"3.10"-"3.11"install:-pipinstallblackscript:-black--check--diff.after_failure:-black--diff.>black_diff.txt

5.4 实战练习:完整的 CI/CD 集成

练习 1:设置 GitHub Actions

创建完整的 GitHub Actions 工作流:

# .github/workflows/black-ci.ymlname:BlackCI/CDon:push:paths:-'**.py'pull_request:paths:-'**.py'jobs:black:runs-on:ubuntu-lateststeps:-uses:actions/checkout@v3-name:InstallBlackrun:pipinstallblack-name:Checkformattingid:black-checkcontinue-on-error:truerun:|          black --check . > black-output.txt 2>&1          echo "status=$?" >> $GITHUB_OUTPUT-name:Generatediffif:steps.black-check.outputs.status!=0run:|          black --diff . > black-diff.txt          cat black-diff.txt-name:Createcommentif:github.event_name=='pull_request'&&steps.black-check.outputs.status!=0uses:actions/github-script@v6with:script:|            const fs = require('fs');            const diff = fs.readFileSync('black-diff.txt', 'utf8');            const comment = `## Black Formatting Issues Found 🖤\n\n\`\`\`diff\n${diff}\n\`\`\``;github.rest.issues.createComment({issue_number:context.issue.number,owner:context.repo.owner,repo:context.repo.repo,body:comment});-name:Failifformattingissuesexistif:steps.black-check.outputs.status!=0run:exit1

练习 2:pre-commit 配置

创建完整的 pre-commit 配置:

# .pre-commit-config.yamlrepos:# 基础钩子-repo:https://github.com/pre-commit/pre-commit-hooksrev:v4.4.0hooks:-id:trailing-whitespace-id:end-of-file-fixer-id:check-yaml-id:check-added-large-files-id:check-merge-conflict-id:debug-statements# Black 格式化-repo:https://github.com/psf/blackrev:23.7.0hooks:-id:blackargs:[--line-length=88]language_version:python3.10# 导入排序-repo:https://github.com/pycqa/isortrev:5.12.0hooks:-id:isortname:isort(python)args:["--profile","black","--filter-files"]# Flake8 检查-repo:https://github.com/pycqa/flake8rev:6.1.0hooks:-id:flake8args:[--max-line-length=88,--extend-ignore=E203,W503]# Mypy 类型检查-repo:https://github.com/pre-commit/mirrors-mypyrev:v1.4.1hooks:-id:mypyadditional_dependencies:[types-all]args:[--ignore-missing-imports]

练习 3:本地测试

创建一个脚本来测试配置:

#!/bin/bash# test-black-setup.shecho"Testing Black configuration..."# 1. 检查 Black 是否安装if ! command -v black &> /dev/null; thenecho"Black is not installed. Installing..."    pip install blackfi# 2. 检查配置文件if [ -f "pyproject.toml" ]; thenecho"✓ pyproject.toml found"    grep -A 5 "\[tool.black\]" pyproject.tomlelseecho"✗ pyproject.toml not found"fi# 3. 检查 pre-commit 配置if [ -f ".pre-commit-config.yaml" ]; thenecho"✓ pre-commit config found"    grep "black" .pre-commit-config.yamlelseecho"✗ pre-commit config not found"fi# 4. 测试格式化echo"Testing formatting on a sample file..."cat > test_format.py << EOFdef test_func(x,y,z):return x+y+zEOFblack test_format.pyecho"Formatted file:"cat test_format.py# 5. 清理rm test_format.pyecho"Test complete!"

练习 4:团队规范文档

创建团队的 Black 使用规范:

# Black 代码格式化规范## 概述我们团队使用 Black 作为统一的 Python 代码格式化工具。## 配置### 项目配置 (pyproject.toml)```toml[tool.black]line-length = 88target-version = ['py310', 'py311']skip-string-normalization = false

编辑器配置

请确保你的编辑器已配置保存时自动格式化。

Git 钩子

使用 pre-commit 确保提交的代码已格式化:

pre-commit install

工作流程

开发流程

  1. 编写代码
  2. 保存文件(自动格式化)
  3. 提交前 pre-commit 钩子自动运行 Black
  4. 如果失败,修复后重新提交

CI/CD 流程

  • PR 检查:确保所有 Python 文件已格式化
  • 自动修复:主分支自动应用格式化

特殊情况处理

禁用格式化

使用 # fmt: off 和 # fmt: on 禁用部分代码的格式化:

# fmt: offdata = [123,456]# fmt: on

魔法逗号

使用尾随逗号强制垂直格式:

# 强制垂直格式items = [1,2,3,]

故障排除

常见问题

  1. Black 未格式化文件:检查文件是否被排除
  2. 格式化冲突:确保 isort 配置为 profile = "black"
  3. CI 失败:本地运行 black --check . 检查问题

参考资源

  • Black 官方文档
  • 项目 Wiki
---## 第6章:处理特殊情况### 6.1 禁用 Black 格式化#### 基本用法使用 `# fmt: off` 和 `# fmt: on` 注释来禁用格式化:```python# fmt: off# 这段代码不会被 Black 格式化data = [    1, 2, 3,    4, 5, 6,    7, 8, 9]# fmt: on# 后续代码恢复正常格式化result = sum(data)print(f"Sum: {result}")

禁用特定代码块

示例 1:复杂的数学表达式

# 禁用格式化以保持数学表达式的可读性# fmt: offresult = (    (x + y) * (a - b) /    (c + d) * (e - f))# fmt: on

示例 2:数据定义

# 保持数据结构的特定格式# fmt: offCOLORS = {'primary':   '#FF0000','secondary''#00FF00','accent':    '#0000FF',}# fmt: on

示例 3:表格数据

# fmt: offUSER_TABLE = [    ['ID''Name',     'Email',            'Role'],    [1,    'Alice',    'alice@example.com''Admin'],    [2,    'Bob',      'bob@example.com',   'User'],    [3,    'Charlie',  'charlie@example.com''User'],]# fmt: on

在特定行禁用

对于单行,可以使用 # fmt: skip

# 跳过这一行的格式化very_long_line = "This is a very long string that I want to keep as is"# fmt: skip# 这一行会被格式化normal_line = "This will be formatted normally"

禁用特定文件

在 .gitignore 或 pyproject.toml 中排除文件:

[tool.black]exclude = '''/legacy_code/| /generated/| /third_party/| \.min\.py$'''

6.2 处理长字符串和注释

字符串换行

Black 会自动处理长字符串:

# 长字符串会被自动换行long_string = "This is a very long string that exceeds the line length limit and will be wrapped automatically by Black"# 格式化后long_string = ("This is a very long string that exceeds the line length limit and will be ""wrapped automatically by Black")

保持字符串原样

使用 # fmt: off 保持长字符串:

# fmt: offlong_string = "This is a very long string that I want to keep as a single line"# fmt: on

多行字符串

# 多行字符串会保留原始格式docstring = """This is a docstringthat spans multiple linesand should keep its formatting"""# Black 不会修改 docstring 的内容

注释处理

# 短注释保持原样x = 10# 这是一个短注释# 长注释会被换行# This is a very long comment that goes beyond the line length limit and will be wrapped by Black# 格式化后# This is a very long comment that goes beyond the line length limit and will be# wrapped by Black

6.3 处理复杂的表达式和链式调用

链式方法调用

# 链式调用会被格式化result = (    df.query("age > 30")      .groupby("city")      .agg({"salary""mean"})      .sort_values("salary", ascending=False)      .reset_index())# 或者单行(如果足够短)result = df.query("age > 30").groupby("city").agg({"salary""mean"})

复杂的条件表达式

# 复杂条件if (    condition1and condition2and condition3or condition4and condition5):    do_something()# 三元表达式value = ("high"if score > 90else"medium"if score > 70else"low")

嵌套数据结构

# 嵌套列表和字典config = {"database": {"host""localhost","port"5432,"settings": {"pool_size"10"timeout"30},    },"cache": {"backend""redis","host""localhost","port"6379,    },}

6.4 处理类型注解和泛型

基本类型注解

# 函数类型注解defprocess_data(    data: List[Dict[str, Union[int, float, str]]],    options: Optional[Dict[str, Any]] = None,) -> Tuple[bool, str]:pass# 格式化后的类型注解会自动换行defprocess_data(    data: List[Dict[str, Union[int, float, str]]],    options: Optional[Dict[str, Any]] = None,) -> Tuple[bool, str]:pass

复杂泛型

from typing import TypeVar, Generic, ListT = TypeVar("T")classContainer(Generic[T]):def__init__(self, items: List[T]) -> None:        self.items = itemsdefget_items(self) -> List[T]:return self.items# 泛型限制from typing import Union, TypeVarNumber = TypeVar("Number", int, float, complex)defadd_numbers(a: Number, b: Number) -> Number:return a + b

Protocol 和 TypedDict

from typing import Protocol, TypedDictclassDrawable(Protocol):defdraw(self) -> None: ...classPoint(TypedDict):    x: int    y: intdefrender(item: Drawable, position: Point) -> None:    item.draw()

处理类型别名

# 类型别名UserId = intUserData = Dict[str, Union[str, int, List[str]]]# 长类型别名会被换行ComplexType = Union[    List[Dict[str, Union[int, float]]],    Tuple[str, ...],    Callable[[int, str], bool],]

6.5 实战练习:处理复杂场景

练习 1:处理混合格式的代码

创建 mixed_format.py

# 包含各种需要特殊处理的代码import sys, os, jsonfrom typing import List,Dict,Any,Optional,Union# 长字符串LONG_TEXT = "This is a very long string that I want to keep as a single line for performance reasons"# 复杂数据结构CONFIG = {'database':{'host':'localhost','port':5432},'cache':{'backend':'redis','host':'localhost','port':6379,'options':{'max_connections':10,'timeout':30}}}defprocess_data(data:List[Dict[str,Union[int,float,str]]],options:Optional[Dict[str,Any]]=None)->Dict[str,Any]:    result = {}for item in data:if item.get('type') == 'user':            result[item['id']]=itemelif item.get('type') == 'product'and item.get('price',0)>100:            result[item['id']]=itemreturn result# fmt: off# 这个复杂表达式应该保持原样if condition1 and condition2 and condition3 or condition4 and condition5:    do_complex_operation()# fmt: onclassDataProcessor:def__init__(self,items:List[str])->None:        self.items=itemsdefprocess(self)->List[str]:return [item.upper()for item in self.items if item]

应用 Black 并观察变化:

black mixed_format.py

练习 2:使用格式化控制

创建 controlled_format.py

# 演示格式化控制defcalculate_metrics(data):# fmt: off# 保持这个复杂计算的格式    result = (        (data['value1'] * data['factor1'] +         data['value2'] * data['factor2']) /        (data['total'if data['total'] > 0else1)    )# fmt: on# 这个会被格式化    metrics = {'mean': sum(data['values']) / len(data['values']),'std': (sum((x - mean) ** 2for x in data['values']) / len(data['values'])) ** 0.5    }return result, metrics# fmt: skip 可以跳过单行very_long_line = "This is a very long line that I want to keep as a single line"# fmt: skip# 多行字符串保持不变SQL_QUERY = """SELECT users.id, users.name, orders.totalFROM usersJOIN orders ON users.id = orders.user_idWHERE users.active = trueAND orders.created_at > NOW() - INTERVAL '30 days'"""

练习 3:类型注解的优化

创建 type_hints.py

from typing import (    List, Dict, Tuple, Optional, Union, Callable, Any, TypeVar, Generic)from dataclasses import dataclassfrom enum import Enum# 复杂的类型别名JsonValue = Union[str, int, float, bool, None, List['JsonValue'], Dict[str, 'JsonValue']]JsonDict = Dict[str, JsonValue]# 泛型类T = TypeVar('T')K = TypeVar('K')V = TypeVar('V')classCache(Generic[K, V]):def__init__(self, maxsize: int = 100, ttl: Optional[float] = None) -> None:        self._cache: Dict[K, V] = {}        self._maxsize = maxsize        self._ttl = ttldefget(self, key: K, default: Optional[V] = None) -> Optional[V]:return self._cache.get(key, default)defset(self, key: K, value: V) -> None:if len(self._cache) >= self._maxsize:            self._cache.pop(next(iter(self._cache)))        self._cache[key] = value# ProtocolclassSerializable(Protocol):defserialize(self) -> JsonDict: ...    @classmethoddefdeserialize(cls, data: JsonDict) -> 'Serializable': ...# TypedDictclassUserData(TypedDict):    id: int    name: str    email: str    roles: List[str]    metadata: JsonDict

运行 Black 并检查结果:

black type_hints.py

练习 4:实际项目中的特殊场景

创建一个模拟真实项目的文件 real_world.py

"""实际项目中的特殊场景处理"""import asyncioimport aiohttpfrom typing import List, Dict, Optionalimport logginglogger = logging.getLogger(__name__)classAPIClient:"""API 客户端,演示异步和复杂逻辑"""def__init__(self, base_url: str, timeout: int = 30):        self.base_url = base_url.rstrip('/')        self.timeout = timeout        self.session: Optional[aiohttp.ClientSession] = Noneasyncdef__aenter__(self):        self.session = aiohttp.ClientSession(            timeout=aiohttp.ClientTimeout(total=self.timeout)        )return selfasyncdef__aexit__(self, exc_type, exc_val, exc_tb):if self.session:await self.session.close()# fmt: off# 这个长 URL 构建应该保持可读性def_build_url(self, endpoint: str, params: Dict[str, str]) -> str:return (f"{self.base_url}/{endpoint.lstrip('/')}"f"?{'&'.join(f'{k}={v}'for k, v in params.items())}"        )# fmt: onasyncdeffetch(self, endpoint: str, params: Optional[Dict] = None) -> Dict:"""获取数据,演示类型注解和错误处理"""        params = params or {}        url = self._build_url(endpoint, {k: str(v) for k, v in params.items()})try:asyncwith self.session.get(url) as response:                response.raise_for_status()returnawait response.json()except aiohttp.ClientError as e:            logger.error(f"Failed to fetch {url}{e}")raiseexcept asyncio.TimeoutError as e:            logger.error(f"Timeout fetching {url}{e}")raise# fmt: off# 保持正则表达式模式的可读性EMAIL_PATTERN = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'URL_PATTERN = r'^(https?://)?([\da-z.-]+)\.([a-z.]{2,6})([/\w .-]*)*/?$'# fmt: on# 复杂的条件判断defvalidate_user_data(user_data: Dict) -> List[str]:    errors = []ifnot user_data.get('email'):        errors.append('Email is required')elifnot EMAIL_PATTERN.match(user_data['email']):        errors.append('Invalid email format')ifnot user_data.get('age'):        errors.append('Age is required')elif user_data['age'] < 0or user_data['age'] > 150:        errors.append('Age must be between 0 and 150')if user_data.get('password'):if len(user_data['password']) < 8:            errors.append('Password must be at least 8 characters')elifnot any(c.isupper() for c in user_data['password']):            errors.append('Password must contain at least one uppercase letter')elifnot any(c.isdigit() for c in user_data['password']):            errors.append('Password must contain at least one digit')return errors

这个练习展示了:

  • 异步代码的格式化
  • 复杂条件语句
  • 正则表达式处理
  • 特殊场景的格式控制


作者简介:码上工坊,探索用编程为己赋能,定期分享编程知识和项目实战经验。持续学习、适应变化、记录点滴、复盘反思、成长进步。

重要提示:本文主要是记录自己的学习与实践过程,所提内容或者观点仅代表个人意见,只是我以为的,不代表完全正确,欢迎交流讨论。

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-03-27 18:41:50 HTTP/2.0 GET : https://f.mffb.com.cn/a/482173.html
  2. 运行时间 : 0.204175s [ 吞吐率:4.90req/s ] 内存消耗:4,779.31kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=aa1df9a23882a2efc1fbb66534bdf81a
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000951s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001594s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000746s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000684s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001686s ]
  6. SELECT * FROM `set` [ RunTime:0.000680s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001536s ]
  8. SELECT * FROM `article` WHERE `id` = 482173 LIMIT 1 [ RunTime:0.001410s ]
  9. UPDATE `article` SET `lasttime` = 1774608110 WHERE `id` = 482173 [ RunTime:0.006506s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 66 LIMIT 1 [ RunTime:0.000617s ]
  11. SELECT * FROM `article` WHERE `id` < 482173 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.001096s ]
  12. SELECT * FROM `article` WHERE `id` > 482173 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.000935s ]
  13. SELECT * FROM `article` WHERE `id` < 482173 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.001770s ]
  14. SELECT * FROM `article` WHERE `id` < 482173 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.011600s ]
  15. SELECT * FROM `article` WHERE `id` < 482173 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.008596s ]
0.207932s