Black 代码风格的(未来)

预览风格

实验性的、可能具有破坏性的风格更改被收集在 --preview CLI 标志下。在每年的年底,这些更改可能会被采用到默认风格中,如 Black 代码风格 中所述。由于该功能是实验性的,因此鼓励反馈和问题报告!

在过去,预览风格包含了一些已知有 bug 的功能,因此我们无法将这些功能迁移到稳定风格中。因此,此类功能现在已被迁移到 --unstable 风格中。--preview 风格中的所有功能预计将在下一年的稳定风格中发布;--unstable 风格中的功能只有在修复了其问题后才会被稳定。如果在 --preview 功能中发现了 bug,它将降级到 --unstable 风格中。为了避免在功能从 --preview 降级到 --unstable 风格时出现混乱,用户可以使用 --enable-unstable-feature 标志来启用特定的不稳定功能。

目前,预览风格中包含以下功能

  • hex_codes_in_unicode_sequences:规范化字符串中 Unicode 转义字符的大小写

  • unify_docstring_detection:修复某些字符串是否被检测为文档字符串的不一致性

  • no_normalize_fmt_skip_whitespace# fmt: skip 注释之前的空白不再被规范化

  • typed_params_trailing_comma:始终为类型化的函数参数添加尾部逗号

  • is_simple_lookup_for_doublestar_expression:修复某些包含幂运算符的表达式的行长计算

  • docstring_check_for_newline:检查文档字符串的结束引号之前是否有换行符

  • remove_redundant_guard_parens:删除 case 块的 if 保护中的冗余括号。

  • parens_for_long_if_clauses_in_case_block:当行过长时,为 case 块中的 if 子句添加括号

不稳定风格还包含以下功能

  • string_processing:分割长字符串文字和相关更改(见下文

  • wrap_long_dict_values_in_parens:在字典中添加长值的括号(见下文

  • multiline_string_handling:更紧凑地格式化涉及多行字符串的表达式(见下文

  • hug_parens_with_braces_and_square_brackets:更紧凑地格式化嵌套括号(见下文

为唯一的函数参数改进多行字典和列表缩进

为了提高可读性和减少垂直性,*Black* 现在将括号(“(”, “)”)与大括号(“{”, “}”)和方括号(“ [”, “]”)放在同一行上。例如

foo(
    [
        1,
        2,
        3,
    ]
)

nested_array = [
    [
        1,
        2,
        3,
    ]
]

将更改为

foo([
    1,
    2,
    3,
])

nested_array = [[
    1,
    2,
    3,
]]

这也适用于列表和字典解包

foo(
    *[
        a_long_function_name(a_long_variable_name)
        for a_long_variable_name in some_generator
    ]
)

将变为

foo(*[
    a_long_function_name(a_long_variable_name)
    for a_long_variable_name in some_generator
])

你可以使用魔术尾部逗号来避免这种压缩行为;默认情况下,*Black* 不会重新格式化以下代码

foo(
    [
        1,
        2,
        3,
    ],
)

改进的字符串处理

*Black* 将分割长字符串文字并合并短字符串文字。在适当的情况下使用括号。当分割时,不需要格式化的 f-字符串的部分将转换为普通字符串。当用户创建的分割不超过行长限制时,这些分割将被保留。行延续反斜杠将被转换为带括号的字符串。不必要的括号将被删除。此功能的稳定性和状态在 此问题 中跟踪。

字典中改进的括号管理

对于具有长值的字典文字,现在它们将被包裹在括号中。现在将删除不必要的括号。例如

my_dict = {
    "a key in my dict": a_very_long_variable
    * and_a_very_long_function_call()
    / 100000.0,
    "another key": (short_value),
}

将更改为

my_dict = {
    "a key in my dict": (
        a_very_long_variable * and_a_very_long_function_call() / 100000.0
    ),
    "another key": short_value,
}

改进的多行字符串处理

*Black* 在格式化多行字符串时更加智能,尤其是在函数参数中,以避免引入额外的换行符。以前,它总是将多行字符串视为无法放在一行上。有了这个新功能,*Black* 会查看多行字符串周围的上下文,以决定是将其内联还是拆分到单独的行上。例如,当将多行字符串传递给函数时,*Black* 只有在行过长或传递了多个参数时才会分割多行字符串。

例如,*Black* 将重新格式化

textwrap.dedent(
    """\
    This is a
    multiline string
"""
)

textwrap.dedent("""\
    This is a
    multiline string
""")

以及

MULTILINE = """
foobar
""".replace(
    "\n", ""
)

MULTILINE = """
foobar
""".replace("\n", "")

隐式多行字符串是特殊的,因为它们可以包含内联注释。没有注释的字符串将被合并,例如

s = (
    "An "
    "implicit "
    "multiline "
    "string"
)

变为

s = "An implicit multiline string"

字符串的任何行上的注释(或两个字符串行之间)将阻止合并,因此

s = (
    "An "  # Important comment concerning just this line
    "implicit "
    "multiline "
    "string"
)

以及

s = (
    "An "
    "implicit "
    # Comment in between
    "multiline "
    "string"
)

将不会被合并。在字符串行之后或之前(但仍然在括号内)添加注释将合并字符串。例如

s = (  # Top comment
    "An "
    "implicit "
    "multiline "
    "string"
    # Bottom comment
)

变为

s = (  # Top comment
    "An implicit multiline string"
    # Bottom comment
)

潜在的未来更改

本节列出我们可能希望在未来进行的更改,但尚未实施。

使用反斜杠进行 with 语句

反斜杠很糟糕,永远不应该使用,但是有一个例外:使用多个上下文管理器进行的 with 语句。在 Python 3.9 之前,Python 的语法不允许在上下文管理器序列周围组织括号。

我们不希望格式化成

with make_context_manager1() as cm1, make_context_manager2() as cm2, make_context_manager3() as cm3, make_context_manager4() as cm4:
    ...  # nothing to split on - line too long

因此,当我们实施此功能时,*Black* 将格式化成这样

with \
     make_context_manager1() as cm1, \
     make_context_manager2() as cm2, \
     make_context_manager3() as cm3, \
     make_context_manager4() as cm4 \
:
    ...  # backslashes and an ugly stranded colon

虽然当目标版本为 Python 3.9 或更高版本时,*Black* 在 --preview 模式下(见下文)使用括号,因为它们在 Python 3.9 及更高版本中是允许的。

如果上述格式中的反斜杠不可取,可以考虑的替代方案是使用 contextlib.ExitStack 以以下方式组合上下文管理器

with contextlib.ExitStack() as exit_stack:
    cm1 = exit_stack.enter_context(make_context_manager1())
    cm2 = exit_stack.enter_context(make_context_manager2())
    cm3 = exit_stack.enter_context(make_context_manager3())
    cm4 = exit_stack.enter_context(make_context_manager4())
    ...