1. 程式人生 > 實用技巧 >python 格式化輸出詳解(佔位符:%、format、f表示式)——上篇 理論篇

python 格式化輸出詳解(佔位符:%、format、f表示式)——上篇 理論篇

0 - 佔位符介紹

要實現字串的拼接,使用佔位符是的一種高效、常用的方式。

舉個例子,下面是不使用佔位符的一種寫法,直接使用加號拼接字串

name = "Li hua"
age = 24
print("Hello "+name+", you are " + str(age) + " years old")

換成使用佔位符的,可以寫為

name = "Li hua"
age = 24
print("Hello %s, you are %d years old" % (name, age))

其中%s%d便是佔位符,顧名思義,其作用就是替後面的變數站住這個位置,
字串後面的%是一個特殊的操作符,該操作符會將後面的變數值,替換掉前面字串中的佔位符。

對比兩種寫法,會發現使用佔位符可以

  • 將字串中用到變數集中在一起,方便查詢和修改
  • 避免了反覆使用引號,導致的引號對應識別困難
  • 能夠更直接通順的看出句子的內容

實際上,佔位符的優點還有很多,具體可以在下面的使用中去體會。
目前常用的佔位符寫法有三種

  • %
  • format
  • f表示式

每種方法下,佔位符的寫法和意思又有不同。

下面依次介紹下這三種並給出幾個使用示例。

1 - %

參考文獻:
% (String Formatting Operator)
printf-style-string-formatting

上文已介紹過,%是一個特殊的操作符,該操作符會將後面的變數值,替換掉前面字串中的佔位符。

其詳細語法格式如下:

"... %[key][flags][width][.precision][length type]conversion type ..." % values

其中

%[key][flags][width][.precision][length type]conversion type

是該方法下,佔位符詳細語法的格式。

依次介紹下上面佔位符每個符號每個欄位的意思

  • %: 必須要有的符號。它標記佔位符的開始。
  • key: 選填。對映的鍵,由帶括號的字元序列組成,一般用於後面的values是是字典的場景。
  • flags: 選填。轉換標誌(Conversion flags), 會影響某些轉換型別的結果。
  • width: 選填。最小欄位寬度。如果指定為“*”(星號),則實際寬度從值中元組的下一個元素讀取,要轉換的物件位於最小欄位寬度和可選精度之後。
  • precision: 選填。精度,寫法為.precision(點+精度)。如果指定為“*”(星號),則實際寬度從值中元組的下一個元素讀取,要轉換的值位於精度之後。
  • length type: 選填。長度修改器。
  • Conversion type: 必須要有的符號。轉換型別,也標記佔位符的開始。

下面依次使用一個小示例展示下上面每個欄位的用法

  • Conversion type

由於這個欄位是必選欄位,所以最先介紹(%寫法是固定的,Conversion type則必須要選擇一個轉換型別)
型別有很多,只介紹三個非常常用的,(更多的建議查閱官方文件:printf-style-string-formatting

Conversion type 說明
s 字串(使用str()方法轉換任何Python物件)
d 十進位制整數
f 十進位制浮點數(小數), 自動保留六位小數。

示例:

>>> "%s  %s  %s" % ("hello", 3, 3.1415)
'hello  3  3.1415'
>>> "%s  %d  %d" % ("hello", 3, 3.1415)
'hello  3  3'
>>> "%s  %d  %f" % ("hello", 3, 3.1415)
'hello  3  3.141500'
>>> "%s  %f  %f" % ("hello", 3, 3.1415)
'hello  3.000000  3.141500'

觀察上面的示例,不難看出s是一個非常通用的型別,所以很多不講究的場景,Conversion type我通通都用s

  • precision
    對於有小數的場景,設定精度是基本操作。
    其寫法為.precision(點+精度)。
    不設定的話,浮點數預設精度值是6。

示例如下

>>> '%f' % 3.14
'3.140000'
>>> '%.1f' % 3.14
'3.1'
>>> '%.2f' % 3.14
'3.14'
>>> '%.3f' % 3.14
'3.140'
>>> '%.4f' % 3.14
'3.1400'

一般來說,%操作符下佔位符瞭解到這裡就夠了,下面的是比較少用的生僻內容。而且也不實用,複雜的對齊操作推薦使用formatf表示式。

  • key (不常用)

這個選填欄位是搭配字典格式的values使用的
示例如下

>>> "%(name)s  %(age)s" % {"name": "Lihua", "age": 20}
'Lihua  20'
>>> "%(name)s  %(age)s" % ({"name": "Lihua", "age": 20})
'Lihua  20'
>>> "%(0)s  %(1)s" % ("Lihua", 20)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: format requires a mapping
  • flags (不常用)

該型別可選擇的值有:#0- +;
這裡只介紹其中幾種,(更多的建議查閱官方文件:printf-style-string-formatting

flags 說明
0 數值的轉換將被零填充,需搭配width使用(示例見下面的width中的)。
- 轉化結果左對齊,需搭配width使用(示例見下面的width中的), 該標誌符會覆蓋0標誌符。
空格, 在帶符號的轉換產生的正數(或空字串), 之前留一個空格(方便正負數最後對齊)。
+ 轉換數字後,數字前面將會展示其正負號(“+”或“-”), 該標誌符會覆蓋 標誌符。

示例如下

>>> "% d  %+d" % (1234, 1234)
' 1234  +1234'
>>> "% d  %+d" % (-1234, -1234)
'-1234  -1234'
  • width
    設定欄位的最小佔位寬度,預設右對齊,內容不夠時使用空格填充。
>>> "%4d,%6d,%10f" % (12, 1234, 3.14)
'  12,  1234,  3.140000'
>>> "%04d,%06d,%010f" % (12, 1234, 3.14)  # use '0' flag
'0012,001234,003.140000'
>>> "%-4d,%-6d,%-10f" % (12, 1234, 3.14)  # use '-' flag
'12  ,1234  ,3.140000  '
>>> "%0-4d,%0-6d,%0-10f" % (12, 1234, 3.14)  # '-' flag will override '0' flag
'12  ,1234  ,3.140000  '
  • length type (使用方式未知)

2 - format

參考文獻:https://docs.python.org/3.6/library/string.html#format-string-syntax
注意:該方法,在python2和3中的語法有些細微的差異,這裡介紹python3.6版本的(應該python3都差不多),具體區別查閱對應版本的官方文件。

str.format()是Python2.6開始的新功能,是字串格式化方法之一,它允許多個替換、值格式化。
這個方法允許我們通過位置,格式化連線字串中的元素。
這個方法是一個非常實用且強大的方法。
對於複雜的對齊要求,首選該方法。

其總的語法格式如下

"... {[field_name][!conversion][:format_spec]} ...".format(arguments)

arguments

首先介紹下arguments,其有兩種情況:

  • 位置引數(Positional Arguments)
>>> "{} {}".format("Li hua", 24)
'Li hua 24'
>>> "{0} {1} {1} {0}".format("Li hua", 24)
'Li hua 24 24 Li hua'
>>> "{} {} {} {}".format("Li hua", 24)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
IndexError: tuple index out of range
  • 關鍵字引數(Keyword Arguments)
>>> "{name} {age}".format(name="Li hua", age=24)
'Li hua 24'
>>> "{name} {age} {age} {name}".format("Li hua", 24)
'Li hua 24 24 Li hua'
>>> "{} {}".format(name="Li hua", age=24)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
IndexError: tuple index out of range

補充:其實位置引數和關鍵字引數可以混用,但是不推薦

然後介紹下該語法下的佔位符格式:

{[field_name][!conversion][:format_spec]}
  • field_name: 選填。欄位名,常使用其基礎格式arg_name來指定使用arguments哪一個。
    對於關鍵詞引數,arg_name必須為其中的關鍵字,(此時該欄位是必填項)
    比如"{name} {age}".format(name="Li hua", age=24)
    對於位置引數,arg_name必須為序號,(此時該欄位可不填,不填則預設第一個為0,從前往後依次+1)
    比如"{0} {1}".format("Li hua", 24)"{} {}".format("Li hua", 24)
    兩者效果一樣。

拓展:該欄位完整語法格式為arg_name(.attribute_name | [element_index])*,是在arg_name對應的值為物件、列表或字典時使用,獲取其進一步的屬性值或者內部值。
這裡舉一個例子:

>>> "{0[name]} {0[age]}, {1[0]} {1[1]}".format({"name": "Li hua", "age": 24}, ["Zhang san",24])
'Li hua 24, Zhang san 24'

conversion

選填。變換,不常用
指定時要用!來開頭,指定後會在格式化之前將arguments中對應的值進行型別變換。
其有三個值可以指定,分別為

conversion 說明
s 呼叫結果物件的str方法進行轉換
r 呼叫結果物件的repr方法進行轉換
a 呼叫結果物件的ascii方法進行轉換

format_spec

選填,格式化具體規範,核心內容,超常用
填寫時要用:來開頭,填寫後,會按照其指定的規則來進行格式化。

其詳細語法為

[[fill]align][sign][#][0][width][grouping_option][.precision][type]

其中所有欄位均為選填,下面依次介紹下(其中加粗的為常用),

  • fill: 填充內容,如果指定了寬度,但變數長度不夠,會使用該欄位值進行填充。設定了fill,後面必須顯式設定align。
  • align: 對齊方式,有以下值:
align 說明
< 強制左對齊(絕大多數物件預設使用)
> 強制右對齊(數字型別預設使用)
= 強制將填充內容放在符號(如果有)之後但數字之前,比如輸出成+000000120這樣的格式。此對齊選項僅對數字型別有效。(當'0'緊接在欄位寬度width之前時,它將成為預設值。)
^ 強制居中對齊
  • sign: 符號展現格式,僅對數字型別有效。有以下值:
sign 說明
+ 正數負數都展現符號,正數用+,負數用-。
- (預設值),僅負數展現符號。
負數展現符號,正數前面使用一個空格來佔位對齊。
  • #: 複雜生僻,基本不使用,不介紹,有需要的可查閱官方文件(見本部分開頭)。

  • 0: 當沒有設定對齊方式align時, 在寬度欄位前面加一個零('0')字元,將等價於填充字元fill0且對齊方式align<

  • width: 最小欄位寬度,不設定則欄位寬度將始終與填充它的資料長度相同(此時對齊方式align沒有意義)。

  • grouping_option: 分組選擇,有兩個選項可選:

grouping_option 說明
, 表示使用逗號作為千位分隔符。
_ 複雜生僻,基本不使用,不介紹.
  • precision: 精度,指定時要用.來開頭,是一個十進位制數,指定用'f'和'f'格式化的浮點值在小數點後應該顯示多少位,即保留幾位小數。

  • type: 型別,決定資料應該如何顯示。
    有很多值,這裡只介紹幾個常用的:

type 說明
s 字串格式。這是字串的預設型別,可以省略(不填)
d 十進位制整數
f 十進位制浮點數(小數), 預設保留六位小數

補充說明1: fill, align只有設定了width才能生效。

簡單示例

>>> "{:4}{:6},{:10}".format("1", "2", 3.14)  # set width
'1   2     ,      3.14'
>>> "{:4}{:>6}, {:^10}".format("1", "2", 3.14)  # set width, align
'1        2,    3.14   '
>>> "{:_<4}{:0>6}, {:^10}".format("1", "2", 3.14)  # set width, align, fill
'1___000002,    3.14   '
>>> "{:_<4}{:0>6}, {:^10.4f}".format("1", "2", 3.14)  # set width, align, fill, precision, type
'1___000002,   3.1400  '

3 - f 表示式

參考文件:
https://docs.python.org/3/reference/lexical_analysis.html#formatted-string-literals
http://zetcode.com/python/fstring/

這是從Python 3.6開始的一個新功能。
f表示式(f-string), 又名(formatted string literal), 是字首為“f”或“f”, 用花括號{}包裹替換欄位的字串文字。

其簡易格式為: f'{name} is {age} years old'
其中花括號{}包裹的是替換欄位replacement_field,相當於上面的佔位符,
但是不同於佔位符是先佔住位置最後標明變數進行替換,f表示式裡的替換欄位直接在花括號裡面進行變數替換。
上面的例子就是用name變數值替換{name}欄位,用age變數值替換{age}欄位。

f表示式詳細格式為:

f'(literal_char | {{ | }} | replacement_field)*'
F'(literal_char | {{ | }} | replacement_field)*'

以上說明f表示式中的字串內容,是由任意個literal_char{{}}replacement_field自由組成的。

其中literal_char是除花括號{}外的任意字元或空。
f表示式中要表示花括號{}文字,需要進行轉義,轉義方式為{{, }},

replacement_field是替換欄位,是f表示式的核心。

其格式為

{f_expression[=][!conversion][:format_spec]}
  • 替換欄位由花括號包裹
  • f_expression: 必填內容,常規Python表示式,一般要被圓括號包圍,只有少數注意事項:
    不允許使用空表示式。
    lambda和賦值表示式:=必須用顯式括號括起來。
    替換表示式可以包含換行符(例如在三重引號字串中),但不能包含註釋。
    每個表示式在格式化字串文字出現的上下文中按從左到右的順序進行計算。

其完整格式為:

(conditional_expression | * or_expr) (, conditional_expression | , * or_expr)* [,] | yield_expression

過於複雜,只展示不介紹,詳情可查閱官方文件:https://docs.python.org/3/reference/lexical_analysis.html#grammar-token-f-expression

  • =: 選填(3.8新版功能), 在表示式後新增等號'=', 可以顯示錶達式文字及其求值後的值(在除錯中很有用),。
  • conversion: 選填。轉換,指定時要在開頭新增!,指定後對錶達式求值的結果在格式化之前進行轉換。
    下方示例中, name = "Ståle"
conversion 說明 示例 輸出
s 對結果呼叫str()方法 f"His name is {name!s}." 'His name is Ståle.'
r 對結果呼叫repr()方法 f"His name is {name!r}." "His name is 'Ståle'."
a 對結果呼叫ascii()方法 f"His name is {name!a}." "His name is 'St\xe5le'."
  • format_spec: 格式規範, 和本文第二部分format中的format_spec格式規範是一樣的。
    不過這裡的可以巢狀使用replacement_field指定其中的值。
>>> line = "The output will have the expression text"
>>> f"{line = }"  # use "=" sign, require python 3.8 or above
'line = "The output will have the expression text"'
>>> width = 10
>>> precision = 4
>>> value = 12.34567
>>> f"result: {value:{10}.{4}}"  # set format_spec
'result:      12.35'
>>> f"result: {value:{width}.{precision}}"  # nested fields
'result:      12.35'