Python代码格式化工具
项目描述
YAPF
简介
YAPF是一个基于clang-format
(由Daniel Jasper开发)的Python格式化工具。本质上,该算法将代码和计算最佳格式,以符合配置的样式。它大大减少了维护代码的繁琐工作。
最终目标是YAPF生成的代码与程序员遵循样式指南编写的代码一样好。
注意 YAPF不是官方的谷歌产品(实验性或其他),它只是谷歌拥有的代码。
安装
从PyPI安装YAPF
$ pip install yapf
YAPF仍然处于“beta”阶段,发布版本可能会经常更改;因此,保持与最新发展的同步的最佳方式是克隆此存储库。
注意,如果您打算将YAPF用作命令行工具而不是库,则不需要安装。YAPF支持由Python解释器作为目录运行。如果您已将YAPF克隆/解压缩到DIR
,则可以运行
$ PYTHONPATH=DIR python DIR/yapf [options] ...
所需的Python版本
YAPF支持Python 3.7+。
注意 YAPF要求它格式化的代码必须是YAPF自身运行的Python版本的合法Python代码。
用法
usage: yapf [-h] [-v] [-d | -i | -q] [-r | -l START-END] [-e PATTERN]
[--style STYLE] [--style-help] [--no-local-style] [-p] [-m] [-vv]
[files ...]
Formatter for Python code.
positional arguments:
files reads from stdin when no files are specified.
optional arguments:
-h, --help show this help message and exit
-v, --version show program's version number and exit
-d, --diff print the diff for the fixed source
-i, --in-place make changes to files in place
-q, --quiet output nothing and set return value
-r, --recursive run recursively over directories
-l START-END, --lines START-END
range of lines to reformat, one-based
-e PATTERN, --exclude PATTERN
patterns for files to exclude from formatting
--style STYLE specify formatting style: either a style name (for
example "pep8" or "google"), or the name of a file
with style settings. The default is pep8 unless a
.style.yapf or setup.cfg or pyproject.toml file
located in the same directory as the source or one of
its parent directories (for stdin, the current
directory is used).
--style-help show style settings and exit; this output can be saved
to .style.yapf to make your settings permanent
--no-local-style don't search for local style definition
-p, --parallel run YAPF in parallel when formatting multiple files.
-m, --print-modified print out file names of modified files
-vv, --verbose print out file names while processing
在您的首选编辑器中使用YAPF
YAPF通过社区扩展或插件被多个编辑器支持。有关更多信息,请参阅编辑器支持。
返回码
通常,YAPF在程序成功终止时返回零,否则返回非零。
如果提供了--diff
,当不需要更改时,YAPF返回零,否则返回非零(包括程序错误)。您可以在CI工作流程中使用此功能来测试代码是否已被YAPF格式化。
排除文件进行格式化 (.yapfignore 或 pyproject.toml)
除了命令行提供的排除模式外,YAPF还会查找在调用YAPF的工作目录中名为.yapfignore
或pyproject.toml
的文件中指定的其他模式。
.yapfignore
的语法类似于UNIX的文件名模式匹配
* matches everything
? matches any single character
[seq] matches any character in seq
[!seq] matches any character not in seq
请注意,条目不应以./
开头。
如果您使用pyproject.toml
,则排除模式由[tool.yapfignore]
部分的ignore_patterns
键指定。例如
[tool.yapfignore]
ignore_patterns = [
"temp/**/*.py",
"temp2/*.py"
]
格式化样式
YAPF使用的格式化样式可配置,并且有许多“旋钮”可以用来调整YAPF的格式化方式。有关完整列表,请参阅style.py
模块。
要控制样式,请使用--style
参数运行YAPF。它接受预定义样式之一(例如,pep8
或google
)、指定所需样式的配置文件路径或键/值对的字典。
配置文件是一个简单的(不区分大小写)key = value
对列表,具有一个[style]
标题。例如
[style]
based_on_style = pep8
spaces_before_comment = 4
split_before_logical_operator = true
based_on_style
设置确定此自定义样式基于哪个预定义样式(可以将其视为子类化)。有四种预定义样式
pep8
(默认)google
(基于谷歌Python样式指南)yapf
(用于谷歌开源项目)facebook
有关详细信息,请参阅style.py
中的_STYLE_NAME_TO_FACTORY
。
您也可以使用字典在命令行上执行相同的操作。例如
--style='{based_on_style: pep8, indent_width: 2}'
这将采用pep8
基本样式并将其修改为两个空格缩进。
YAPF将以以下方式搜索格式化样式
- 在命令行上指定
- 在当前目录或其父目录中的
.style.yapf
文件的[style]
部分。 - 在当前目录或其父目录中的
setup.cfg
文件的[yapf]
部分。 - 在当前目录或其父目录中的
pyproject.toml
文件的[tool.yapf]
部分。 - 在您的家目录中的
~/.config/yapf/style
文件的[style]
部分。
如果找不到这些文件,则使用默认样式PEP8。
示例
YAPF可以处理的格式化类型示例,它将把以下难看的代码
x = { 'a':37,'b':42,
'c':927}
y = 'hello ''world'
z = 'hello '+'world'
a = 'hello {}'.format('world')
class foo ( object ):
def f (self ):
return 37*-+2
def g(self, x,y=42):
return y
def f ( a ) :
return 37+-+a[42-x : y**3]
重新格式化为
x = {'a': 37, 'b': 42, 'c': 927}
y = 'hello ' 'world'
z = 'hello ' + 'world'
a = 'hello {}'.format('world')
class foo(object):
def f(self):
return 37 * -+2
def g(self, x, y=42):
return y
def f(a):
return 37 + -+a[42 - x:y**3]
作为模块的示例
调用YAPF的两种主要API是FormatCode
和FormatFile
,它们共享以下描述的几个参数
>>> from yapf.yapflib.yapf_api import FormatCode # reformat a string of code
>>> formatted_code, changed = FormatCode("f ( a = 1, b = 2 )")
>>> formatted_code
'f(a=1, b=2)\n'
>>> changed
True
style_config
参数:一个样式名称或包含格式化样式设置的文件的路径。如果没有指定,则使用在style.DEFAULT_STYLE_FACTORY
中设置的默认样式。
>>> FormatCode("def g():\n return True", style_config='pep8')[0]
'def g():\n return True\n'
lines
参数:我们想要格式化的行的元组列表(整数),[开始,结束]。行是1为基础索引的。第三方代码(例如,IDEs)在重新格式化代码片段而不是整个文件时可以使用。
>>> FormatCode("def g( ):\n a=1\n b = 2\n return a==b", lines=[(1, 1), (2, 3)])[0]
'def g():\n a = 1\n b = 2\n return a==b\n'
print_diff
(布尔值):返回重格式化后的源代码,而不是返回将格式化后的源代码转换为重格式化后的源代码的diff。
>>> print(FormatCode("a==b", filename="foo.py", print_diff=True)[0])
--- foo.py (original)
+++ foo.py (reformatted)
@@ -1 +1 @@
-a==b
+a == b
注意:FormatCode
的filename
参数是插入到diff中的内容,默认值为<unknown>
。
FormatFile
返回传递文件的格式化代码及其编码
>>> from yapf.yapflib.yapf_api import FormatFile # reformat a file
>>> print(open("foo.py").read()) # contents of file
a==b
>>> reformatted_code, encoding, changed = FormatFile("foo.py")
>>> formatted_code
'a == b\n'
>>> encoding
'utf-8'
>>> changed
True
in_place
参数将重格式化后的代码保存回文件
>>> FormatFile("foo.py", in_place=True)[:2]
(None, 'utf-8')
>>> print(open("foo.py").read()) # contents of file (now fixed)
a == b
格式化diff
选项
usage: yapf-diff [-h] [-i] [-p NUM] [--regex PATTERN] [--iregex PATTERN][-v]
[--style STYLE] [--binary BINARY]
This script reads input from a unified diff and reformats all the changed
lines. This is useful to reformat all the lines touched by a specific patch.
Example usage for git/svn users:
git diff -U0 --no-color --relative HEAD^ | yapf-diff -i
svn diff --diff-cmd=diff -x-U0 | yapf-diff -p0 -i
It should be noted that the filename contained in the diff is used
unmodified to determine the source file to update. Users calling this script
directly should be careful to ensure that the path in the diff is correct
relative to the current working directory.
optional arguments:
-h, --help show this help message and exit
-i, --in-place apply edits to files instead of displaying a diff
-p NUM, --prefix NUM strip the smallest prefix containing P slashes
--regex PATTERN custom pattern selecting file paths to reformat
(case sensitive, overrides -iregex)
--iregex PATTERN custom pattern selecting file paths to reformat
(case insensitive, overridden by -regex)
-v, --verbose be more verbose, ineffective without -i
--style STYLE specify formatting style: either a style name (for
example "pep8" or "google"), or the name of a file
with style settings. The default is pep8 unless a
.style.yapf or setup.cfg or pyproject.toml file
located in the same directory as the source or one of
its parent directories (for stdin, the current
directory is used).
--binary BINARY location of binary to use for YAPF
旋钮
ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT
将对齐关闭括号与视觉缩进。
ALLOW_MULTILINE_LAMBDAS
允许lambda在多行中格式化。
ALLOW_MULTILINE_DICTIONARY_KEYS
允许字典键存在于多行中。例如
x = {
('this is the first element of a tuple',
'this is the second element of a tuple'):
value,
}
ALLOW_SPLIT_BEFORE_DEFAULT_OR_NAMED_ASSIGNS
允许在参数列表中的默认/命名赋值之前分割。
ALLOW_SPLIT_BEFORE_DICT_VALUE
允许在字典值之前分割。
ARITHMETIC_PRECEDENCE_INDICATION
让间距表示操作符优先级。例如
a = 1 * 2 + 3 / 4
b = 1 / 2 - 3 * 4
c = (1 + 2) * (3 - 4)
d = (1 - 2) / (3 + 4)
e = 1 * 2 - 3
f = 1 + 2 + 3 + 4
将被格式化为以下内容以表示优先级
a = 1*2 + 3/4
b = 1/2 - 3*4
c = (1+2) * (3-4)
d = (1-2) / (3+4)
e = 1*2 - 3
f = 1 + 2 + 3 + 4
BLANK_LINES_AROUND_TOP_LEVEL_DEFINITION
设置围绕顶级函数和类定义所需的空行数。例如
class Foo:
pass
# <------ having two blank lines here
# <------ is the default setting
class Bar:
pass
BLANK_LINE_BEFORE_CLASS_DOCSTRING
在类级文档字符串之前插入空行。
BLANK_LINE_BEFORE_MODULE_DOCSTRING
在模块文档字符串之前插入空行。
BLANK_LINE_BEFORE_NESTED_CLASS_OR_DEF
在另一个
def
或class
内部立即插入空行,在另一个def
或class
之后。例如
class Foo:
# <------ this blank line
def method():
pass
BLANK_LINES_BETWEEN_TOP_LEVEL_IMPORTS_AND_VARIABLES
设置顶级导入和变量定义之间所需的空行数。对于与isort等工具兼容很有用。
COALESCE_BRACKETS
不要分割连续的括号。仅在设置
DEDENT_CLOSING_BRACKETS
或INDENT_CLOSING_BRACKETS
时相关。例如
call_func_that_takes_a_dict(
{
'key1': 'value1',
'key2': 'value2',
}
)
将重格式化为
call_func_that_takes_a_dict({
'key1': 'value1',
'key2': 'value2',
})
COLUMN_LIMIT
列限制(或最大行长度)
CONTINUATION_ALIGN_STYLE
连续对齐样式。可能的值是
SPACE
:使用空格进行连续对齐。这是默认行为。FIXED
:使用固定列数(即CONTINUATION_INDENT_WIDTH
)进行连续对齐。(即CONTINUATION_INDENT_WIDTH
/INDENT_WIDTH
制表符或CONTINUATION_INDENT_WIDTH
空格)。VALIGN-RIGHT
:垂直对齐连续行到INDENT_WIDTH
列的倍数。如果无法垂直对齐连续行与缩进字符,则稍微向右(一个制表符或几个空格)。
CONTINUATION_INDENT_WIDTH
用于行续行的缩进宽度。
DEDENT_CLOSING_BRACKETS
如果括号内的表达式无法适合一行,则在单独的行上放置关闭括号,并缩进。适用于所有类型的括号,包括函数定义和调用。例如
config = {
'key1': 'value1',
'key2': 'value2',
} # <--- this bracket is dedented and on a separate line
time_series = self.remote_client.query_entity_counters(
entity='dev3246.region1',
key='dns.query_latency_tcp',
transform=Transformation.AVERAGE(window=timedelta(seconds=60)),
start_ts=now()-timedelta(days=3),
end_ts=now(),
) # <--- this bracket is dedented and on a separate line
DISABLE_ENDING_COMMA_HEURISTIC
禁用将每个列表元素放置在单独行的启发式方法,如果列表以逗号终止。
EACH_DICT_ENTRY_ON_SEPARATE_LINE
将每个字典条目放置在其自己的行上。
FORCE_MULTILINE_DICT
即使行短于
COLUMN_LIMIT
,也尊重EACH_DICT_ENTRY_ON_SEPARATE_LINE
。
I18N_COMMENT
国际化注释的正则表达式。如果存在此注释,则停止该行的格式化,因为注释必须紧挨着它们所翻译的字符串。
I18N_FUNCTION_CALL
国际化函数调用名称。如果存在此函数,则停止该行的格式化,因为它所包含的字符串不能被移离国际化注释。
INDENT_BLANK_LINES
设置为
True
以优先使用缩进空白行而不是空行。
INDENT_CLOSING_BRACKETS
如果括号表达式无法放在单行中,则将关闭括号放在单独的一行,并缩进。这适用于所有类型的括号,包括函数定义和调用。例如
config = {
'key1': 'value1',
'key2': 'value2',
} # <--- this bracket is indented and on a separate line
time_series = self.remote_client.query_entity_counters(
entity='dev3246.region1',
key='dns.query_latency_tcp',
transform=Transformation.AVERAGE(window=timedelta(seconds=60)),
start_ts=now()-timedelta(days=3),
end_ts=now(),
) # <--- this bracket is indented and on a separate line
INDENT_DICTIONARY_VALUE
如果字典值无法与字典键放在同一行,则缩进字典值。例如
config = {
'key1':
'value1',
'key2': value1 +
value2,
}
INDENT_WIDTH
用于缩进的列数。
JOIN_MULTIPLE_LINES
将短行合并为一行。例如,单行
if
语句。
NO_SPACES_AROUND_SELECTED_BINARY_OPERATORS
不要在所选的二进制运算符周围包含空格。例如
1 + 2 * 3 - 4 / 5
当配置为
*
或/
时,将格式化为以下形式
1 + 2*3 - 4/5
SPACE_BETWEEN_ENDING_COMMA_AND_CLOSING_BRACKET
在列表等的末尾逗号和关闭括号之间插入空格。
SPACE_INSIDE_BRACKETS
Use spaces inside brackets, braces, and parentheses. For example:
method_call( 1 )
my_dict[ 3 ][ 1 ][ get_index( *args, **kwargs ) ]
my_set = { 1, 2, 3 }
SPACES_AROUND_DEFAULT_OR_NAMED_ASSIGN
设置为
True
以优先使用赋值操作符周围的空格,用于默认或关键字参数。
SPACES_AROUND_DICT_DELIMITERS
在字典分隔符的开头 '{' 和结尾 '}' 之前添加空格。
{1: 2}
将格式化为以下形式
{ 1: 2 }
SPACES_AROUND_LIST_DELIMITERS
在列表分隔符的开头 '[' 和结尾 ']' 之前添加空格。
[1, 2]
将格式化为以下形式
[ 1, 2 ]
SPACES_AROUND_POWER_OPERATOR
设置为
True
以优先使用空格围绕**
。
SPACES_AROUND_SUBSCRIPT_COLON
使用空格围绕下标/切片操作符。例如
my_list[1 : 10 : 2]
SPACES_AROUND_TUPLE_DELIMITERS
在元组分隔符的开头 '(' 和结尾 ')' 之前添加空格。
(1, 2, 3)
将格式化为以下形式
( 1, 2, 3 )
SPACES_BEFORE_COMMENT
在尾随注释之前所需的空格数。这可以是一个单个值(表示每个尾随注释之前的空格数)或值列表(表示对齐列值;块内的尾随注释将对齐到大于块内最大行长的第一个列值)。
注意:在某些上下文中(例如,shell 或编辑器配置文件),值列表可能需要引用。
例如,使用
spaces_before_comment=5
1 + 1 # Adding values
将格式化为以下形式
1 + 1 # Adding values <-- 5 spaces between the end of the statement and comment
使用
spaces_before_comment="15, 20"
1 + 1 # Adding values
two + two # More adding
longer_statement # This is a longer statement
short # This is a shorter statement
a_very_long_statement_that_extends_beyond_the_final_column # Comment
short # This is a shorter statement
将格式化为以下形式
1 + 1 # Adding values <-- end of line comments in block aligned to col 15
two + two # More adding
longer_statement # This is a longer statement <-- end of line comments in block aligned to col 20
short # This is a shorter statement
a_very_long_statement_that_extends_beyond_the_final_column # Comment <-- the end of line comments are aligned based on the line length
short # This is a shorter statement
SPLIT_ALL_COMMA_SEPARATED_VALUES
如果逗号分隔的列表(
dict
、list
、tuple
或函数def
)在太长的行上,则将其拆分为每个元素都在单独一行上。
SPLIT_ALL_TOP_LEVEL_COMMA_SEPARATED_VALUES
是
SPLIT_ALL_COMMA_SEPARATED_VALUES
的变体,其中,如果带有逗号的子表达式可以适合其起始行,则子表达式不会被拆分。这避免了像下面代码中b
的拆分
abcdef(
aReallyLongThing: int,
b: [Int,
Int])
使用这个新旋钮,拆分如下
abcdef(
aReallyLongThing: int,
b: [Int, Int])
SPLIT_ARGUMENTS_WHEN_COMMA_TERMINATED
如果参数列表以逗号终止,则在参数之前拆分。
SPLIT_BEFORE_ARITHMETIC_OPERATOR
设置为
True
以优先在+
、-
、*
、/
、//
或@
之前拆分,而不是之后。
SPLIT_BEFORE_BITWISE_OPERATOR
设置为
True
以优先在&
、|
或^
之前拆分,而不是之后。
SPLIT_BEFORE_CLOSING_BRACKET
如果列表或字典字面量无法放在单行中,则在关闭括号之前拆分。
SPLIT_BEFORE_DICT_SET_GENERATOR
在字典或集合生成器(
comp_for
)之前拆分。例如,注意在for
之前的拆分
foo = {
variable: 'Hello world, have a nice day!'
for variable in bar if variable != 42
}
SPLIT_BEFORE_DOT
如果需要拆分较长的表达式,则在
.
之前拆分
foo = ('This is a really long string: {}, {}, {}, {}'.format(a, b, c, d))
将重新格式化为类似以下的内容
foo = ('This is a really long string: {}, {}, {}, {}'
.format(a, b, c, d))
在表达式周围的括号打开之后进行分割
如果表达式不适合单行,则在括号后的表达式进行分割。
在第一个参数之前分割
如果参数/参数列表将要分割,那么在第一个参数之前进行分割。
在逻辑运算符之前分割
设置为
True
以优先在and
或or
之前而不是之后进行分割。
在命名赋值之前分割
将命名赋值分割到单独的行上。
分割复杂生成器表达式
对于具有多个子句(例如多个
for
调用、if
过滤表达式)的列表生成器和生成器表达式,并且需要重新排列,将每个子句分割到其自己的行上。例如
result = [
a_var + b_var for a_var in xrange(1000) for b_var in xrange(1000)
if a_var % b_var]
将重新格式化为类似以下的内容
result = [
a_var + b_var
for a_var in xrange(1000)
for b_var in xrange(1000)
if a_var % b_var]
在开括号之后分割的惩罚
在开括号后分割的惩罚。
一元运算符之后分割的惩罚
在单元运算符之后分割行的惩罚。
算术运算符分割的惩罚
在
+
、-
、*
、/
、//
、%
和@
运算符周围分割行的惩罚。
在 if
表达式之前分割的惩罚
if
表达式之前分割的惩罚在
if
表达式之前分割的惩罚。
位运算符分割的惩罚
在
&
、|
和^
运算符周围分割行的惩罚。
生成器表达式分割的惩罚
分割列表生成器或生成器表达式的惩罚。
超出字符限制的惩罚
超出列限制的字符的惩罚。
添加行分割的惩罚
添加到逻辑行中的行分割产生的惩罚。添加的行分割越多,惩罚越高。
分割 import as
名称的惩罚
import as
名称的惩罚分割
import as
名称列表的惩罚。例如
from a_very_long_or_indented_module_name_yada_yad import (long_argument_1,
long_argument_2,
long_argument_3)
将重新格式化为类似以下的内容
from a_very_long_or_indented_module_name_yada_yad import (
long_argument_1, long_argument_2, long_argument_3)
逻辑运算符分割的惩罚
在
and
和or
运算符周围分割行的惩罚。
使用制表符
使用制表符进行缩进。
(可能) 常见问题
为什么 YAPF 会破坏我的出色格式化?
YAPF 尽力确保格式化正确。但对于某些代码,它可能不如手动格式化好。特别是,大量数据文字可能在 YAPF 下变得非常丑陋。
这种情况的原因有很多。简而言之,YAPF 只是一个帮助开发的工具。它将格式化与风格指南相一致,但这可能并不等同于可读性。
要减轻这种情况,可以指示 YAPF 应该在重新格式化某些内容时忽略的区域
# yapf: disable
FOO = {
# ... some very large, complex data literal.
}
BAR = [
# ... another large data literal.
]
# yapf: enable
您还可以禁用单个文字的格式化,如下所示
BAZ = {
(1, 2, 3, 4),
(5, 6, 7, 8),
(9, 10, 11, 12),
} # yapf: disable
为了保留漂亮的缩进关闭括号,请在您的风格中使用 dedent_closing_brackets
。注意,在这种情况下,包括函数定义和调用在内的所有括号都将使用该样式。这为格式化的代码库提供了一致性。
为什么不改进现有的工具?
我们想使用 clang-format 的重新格式化算法。它非常强大,旨在提供最好的格式化。现有的工具是为了不同的目标而创建的,需要大量修改才能转换为使用 clang-format 的算法。
我可以在我的程序中使用 YAPF 吗?
当然可以!YAPF 旨在作为库以及命令行工具使用。这意味着工具或 IDE 插件可以自由使用 YAPF。
我仍然得到非 PEP8 兼容的代码!为什么?
YAPF 尽力做到完全符合 PEP 8。然而,不改变代码的语义至关重要。因此,YAPF 尽可能地安全,并且不更改标记流(例如,通过添加括号)。然而,所有这些情况都可以很容易地手动修复。例如,
from my_package import my_function_1, my_function_2, my_function_3, my_function_4, my_function_5
FOO = my_variable_1 + my_variable_2 + my_variable_3 + my_variable_4 + my_variable_5 + my_variable_6 + my_variable_7 + my_variable_8
不会分割,但您可以很容易地通过添加括号来正确地得到它
from my_package import (my_function_1, my_function_2, my_function_3,
my_function_4, my_function_5)
FOO = (my_variable_1 + my_variable_2 + my_variable_3 + my_variable_4 +
my_variable_5 + my_variable_6 + my_variable_7 + my_variable_8)
细节
算法设计
YAPF的主要数据结构是LogicalLine
对象。它包含一个FormatToken
\s列表,如果我们没有列限制,我们希望将它们放置在同一行。例外情况是在表达式语句中间的注释将迫使行在多行上格式化。格式化器一次处理一个LogicalLine
对象。
LogicalLine
通常不会影响其前后行的格式化。算法的一部分可能会将两个或多个LogicalLine
\s合并为一行。例如,具有简短主体的if-then语句可以放置在同一行。
if a == 42: continue
YAPF的格式化算法创建一个加权树,该树作为算法的解空间。树中的每个节点代表一个格式化决策的结果,即是否在标记之前分割。每个格式化决策都与其相关联的成本。因此,成本体现在两个节点之间的边(实际上,加权树没有单独的边对象,因此成本位于节点本身)。
例如,以下是一个Python代码片段。为了这个例子,假设行(1)违反了列限制,需要重新格式化。
def xxxxxxxxxxx(aaaaaaaaaaaa, bbbbbbbbb, cccccccc, dddddddd, eeeeee): # 1
pass # 2
对于行(1),算法将构建一个树,其中每个节点(一个FormattingDecisionState
对象)是给定在标记之前是否分割的决策的该标记的行状态。注意:FormatDecisionState
对象是按值复制的,因此图中的每个节点都是唯一的,一个节点的变化不会影响其他节点。
使用启发式方法来确定分割或不分割的成本。因为节点持有树的状态,直到标记的插入,它可以很容易地确定分割决策是否会违反某些样式要求。例如,启发式方法能够在不分割前一个标记和被添加的标记之间的边应用额外的惩罚。
有些情况下,我们永远不会想要分割行,因为这样做总是有害的(即,它需要反斜杠换行符,这很少是想要的)。对于行(1),我们永远不会想要分割前三个标记:def
、xxxxxxxxxxx
和(
。同样,我们也不想在)
和:
之间分割。这些区域被称为“不可分割的”。这在树中表现为不可分割区域内没有“分割”决策(左分支)。
现在我们有了树,我们通过找到树中成本最低的路径来确定“最佳”格式。
就是这样!
项目详情
下载文件
下载适用于您平台的文件。如果您不确定选择哪个,请了解有关安装包的更多信息。