YAPF 是一個基於clang-format
的 Python 格式化程式(由 Daniel Jasper 開發)。本質上,演算法會取得程式碼並計算符合配置樣式的最佳格式。它消除了維護程式碼的大量苦差事。
最終目標是 YAPF 產生的程式碼與程式設計師遵循風格指南編寫的程式碼一樣好。
請注意, YAPF 不是 Google 的官方產品(實驗性或其他形式),它只是碰巧歸 Google 所有的程式碼。
要從 PyPI 安裝 YAPF:
$ pip install yapf
YAPF仍處於「beta」階段,發布的版本可能會經常變更;因此,了解最新開發的最佳方法是克隆此存儲庫或直接從 github 安裝:
$ pip install git+https://github.com/google/yapf.git
請注意,如果您打算將 YAPF 用作命令列工具而不是庫,則無需安裝。 YAPF 支援由 Python 解釋器作為目錄運作。如果您將 YAPF 克隆/解壓縮到DIR
中,則可以運行:
$ PYTHONPATH=DIR python DIR/yapf [options] ...
YAPF 透過社群擴充或外掛程式得到多個編輯器的支援。請參閱編輯器支援以獲取更多資訊。
YAPF 支援 Python 3.7+。
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 在程式成功終止時傳回零,否則傳回非零。
如果提供了--diff
,則當不需要更改時 YAPF 傳回零,否則傳回非零(包括程式錯誤)。您可以在 CI 工作流程中使用它來測試程式碼是否已採用 YAPF 格式。
除了排除命令列上提供的模式之外,YAPF 還會尋找名為.yapfignore
或pyproject.toml
的檔案中指定的其他模式,該檔案位於呼叫 YAPF 的工作目錄中。
.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
)、指定所需樣式的設定檔的路徑或鍵/值對的字典。
設定檔是帶有[style]
標題的(不區分大小寫) key = value
對的簡單清單。例如:
[style]
based_on_style = pep8
spaces_before_comment = 4
split_before_logical_operator = true
based_on_style
設定決定此自訂樣式是基於哪種預訂樣式(將其視為子類化)。預先定義了四種樣式:
pep8
(預設)google
(基於 Google Python 風格指南)yapf
(用於 Google 開源專案)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 開始索引的。當重新格式化程式碼片段而不是整個檔案時,第三方程式碼(例如,IDE)可以使用它。
> >> 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
(bool):傳回一個將格式化來源轉換為重新格式化來源的 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
選項:
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
如果清單以逗號結尾,則停用啟發式將每個清單元素放在單獨的行上。
注意:此標誌的行為在 v0.40.3 中發生了變化。以前,如果此標誌為 true,我們將拆分包含尾隨逗號或註釋的清單。現在,我們有一個單獨的標誌
DISABLE_SPLIT_LIST_WITH_COMMENT
,它可以在清單包含註解時控制分割。要獲得舊的行為,請將兩個標誌設為 true。更多資訊請參閱 CHANGELOG.md。
DISABLE_SPLIT_LIST_WITH_COMMENT
不要將每個元素放在包含插頁式註解的清單中的新行上。
沒有此標誌(預設):
[ a, b, # c ]
有了這個標誌:
[ a, b, # c ]
這反映了 clang-format 的行為,對於形成列表中元素的「邏輯組」很有用。它也適用於函數聲明。
EACH_DICT_ENTRY_ON_SEPARATE_LINE
將每個字典條目放在自己的行上。
FORCE_MULTILINE_DICT
即使該行短於
COLUMN_LIMIT
也要遵守EACH_DICT_ENTRY_ON_SEPARATE_LINE
。
I18N_COMMENT
國際化註釋的正規表示式。此註解的存在會停止對該行的重新格式化,因為註解需要位於它們翻譯的字串旁邊。
I18N_FUNCTION_CALL
國際化函數呼叫名稱。此函數的存在會停止在該行上重新格式化,因為它所具有的字串無法從 i18n 註解中移開。
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
或 functiondef
)位於太長的行上,則進行拆分,使每個元素位於單獨的行上。
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
如果
list
或dict
文字不適合單行,則在右括號之前拆分。
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 ))
SPLIT_BEFORE_EXPRESSION_AFTER_OPENING_PAREN
如果表達式不適合放在一行中,則在包圍表達式的左括號後進行拆分。
SPLIT_BEFORE_FIRST_ARGUMENT
如果要拆分參數/參數列表,請在第一個參數之前拆分。
SPLIT_BEFORE_LOGICAL_OPERATOR
設定為
True
表示喜歡在and
或or
之後分割,而不是在之後分割。
SPLIT_BEFORE_NAMED_ASSIGNS
將命名作業拆分到單獨的行上。
SPLIT_COMPLEX_COMPREHENSION
對於具有多個子句(例如多個
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 ]
SPLIT_PENALTY_AFTER_OPENING_BRACKET
開局後立即分裂的處罰。
SPLIT_PENALTY_AFTER_UNARY_OPERATOR
在一元運算子之後分割行的懲罰。
SPLIT_PENALTY_ARITHMETIC_OPERATOR
將行拆分為
+
、-
、*
、/
、//
、%
和@
運算子的代價。
SPLIT_PENALTY_BEFORE_IF_EXPR
在
if
表達式之前進行分割的懲罰。
SPLIT_PENALTY_BITWISE_OPERATOR
在
&
,|
周圍分割線的懲罰, 和^
運算符。
SPLIT_PENALTY_COMPREHENSION
拆分列表理解或產生器表達式的懲罰。
SPLIT_PENALTY_EXCESS_CHARACTER
對超出列限制的字元的懲罰。
SPLIT_PENALTY_FOR_ADDED_LINE_SPLIT
由於向邏輯行添加行分割而產生的損失。分線越多,處罰越高。
SPLIT_PENALTY_IMPORT_NAMES
將
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 )
SPLIT_PENALTY_LOGICAL_OPERATOR
在
and
和or
運算子周圍分割線的懲罰。
USE_TABS
使用製表符進行縮排。
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 被設計為用作函式庫和命令列工具。這意味著工具或 IDE 插件可以免費使用 YAPF。
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
和(
。我們也不想在末尾的)
和:
之間拆分。據說這些區域是「牢不可破的」。這在樹中反映為在牢不可破的區域內不存在「分裂」決策(左手分支)。
現在我們有了樹,我們透過尋找成本最低的樹路徑來確定「最佳」格式。
就是這樣!