來到 3.6 版,Python 已經有三種字串格式化的方法。
- printf-style:由於對 tuple 與 dict 支援不夠,建議使用另外兩種方法。
- Formatted string literal:3.6 版新增。
- str.format():最強的字串格式化。
printf-style
官方文件整數後加上 % 是求餘數,字串後加上 % 是格式運算子 Format Operator。
% 前是包含 Conversion specifier 的待格式化字串,% 後是用來取代 Conversion specifier 的變數,視情況可以使用單一變數、tuple 與 dict。
print('I have %d books.' % 3) # I have 3 books.,可以使用單一變數也可以使用一個 item 的 tuple print('In %d months, I have read %d books.' % (3, 10)) # In 3 months, I have read 10 books. print('In %(months)d months, I have read %(books)d books.' % {'months':3, 'books':10}) # In 3 months, I have read 10 books.變數的數量要對,格式也要對,不然會報錯。
Conversion specifier 由兩個以上字元組成,組成解析如下。
- 第一個字元是 %,表示從靜態字串進入 Conversion specifier 宣告
- 非必要的 Mapping key,由小括號和字串組成,例如 (months),當 % 後是 dict 時,就必須使用 Mapping key
- 非必要的 Conversion flags,用來設定某些 Conversion type 的輸出
- 非必要的最小字串長度,直接指名長度或者使用 *,改由 tuple 第一個 item 指定
- 非必要的精確度,必須接在 . 之後,精確度包括整數與小數,直接指名精確度或者使用 *,改由 tuple 第一個 item 指定
- 非必要的 Length modifier,可以使用 h、l 或 L,但沒有任何用處,被 Python 略過
- Conversion type
可以在 Conversion specifier 指定最小字串長度。
print('I have %5d books.' % (105)) # I have 105 books.,直接指名長度 print('I have %*d books.' % (5, 10)) # I have 10 books.,使用 * 表示從 tuple 第一個 item 指定 print('I have %*d books.' % (5, 105)) # I have 105 books.,使用 * 表示從 tuple 第一個 item 指定 print('I have %05d books.' % (105)) # I have 00105 books.,可以在長度前加上 Padding 字元 # print('I have %*d books.' % (05, 10)) # 語法錯誤 05,Padding 字元 不可以放在 tuple 裡 print('I have %0*d books.' % (5, 10)) # I have 00010 books.
可以在 Conversion specifier 指定 Conversion flags。
- # 號:有點複雜
- 數字 0:可以在數值前面加上 Padding 0
- - 號:靠左對齊,右邊留白,可以用在字串與數值上
- 空白:Padding
- + 號:靠右對齊,左邊留白,用在字串上
print('I have %05d books.' % (105)) # I have 00105 books.,可以在長度前加上 Padding 0 # print('I have %*d books.' % (05, 10)) # 語法錯誤 05,0 是 Conversion Flag,不可以放在 tuple 裡 print('I have %0*d books.' % (5, 10)) # I have 00010 books. # 使用 0 或空白 padding print('I have %05d books.' % (105)) # I have 00105 books. print('I have % 5d books.' % (105)) # I have 105 books. # 使用 - 號 print('I have %-5d books.' % (105)) # I have 105 books. 靠左對齊 print('I have %-05d books.' % (105)) # I have 105 books. - 號蓋過數字 0 print('I have %- 5d books.' % (105)) # I have 105 books. 置中對齊? # 使用 + 號 print('I read %+10s.' % ('Python')) # I read Python. print('I read %+ 10s.' % ('Python')) # I read Python. print('I read %+010s.' % ('Python')) # I read Python. print('I read %-10s.' % ('Python')) # I read Python . print('I read %- 10s.' % ('Python')) # I read Python . print('I read %-010s.' % ('Python')) # I read Python .
可以在 Conversion specifier 指定精確度,必須接在 . 之後,精確度包括整數與小數。
print('A precise number [%.6g]' % (123.45)) # A precise number [123.45] print('A precise number [%.5g]' % (123.45)) # A precise number [123.45] print('A precise number [%.4g]' % (123.45)) # A precise number [123.5] print('A precise number [%.3g]' % (123.45)) # A precise number [123] print('A precise number [%.2g]' % (123.45)) # A precise number [1.2e+02] print('A precise number [%.1g]' % (123.45)) # A precise number [1e+02] print('A precise number [%.0g]' % (123.45)) # A precise number [1e+02] print('A precise number [%.*g]' % (4, 123.45)) # A precise number [123.5],使用 * 表示從 tuple 第一個 item 指定
Conversion type,以下列出可能會用到的,其他的請看官方文件。
- d:包括正負號的十進位整數
- i:同 d
- u:同 d,已改用 d
- o:包括正負號的八進位
- x:包括正負號的十六進位(小寫)
- X:包括正負號的十六進位(大寫)
- e:指數顯示浮點數(小寫)
- E:指數顯示浮點數(大寫)
- f:十進位浮點數
- F:同 f
- g:類似 e
- G:類似 E
- c:單一字元,可用整數或單一字元的字串
- r:字串,透過 repr() 轉換成字串
- s:字串,透過 str() 轉換成字串
- a:字串,透過 ascii() 轉換成字串
- %:就是顯示 %
Formatted string literal
官方文件就像一般的字串表示式一樣,只是在單引號或雙引號前面加上 f 或者 F(Raw 字串是加上 r),字串裡的變數用大括號,就可以直接取用當下 scope 的變數,不用再透過 % 餵變數進去。
books = 3 print('I have {books} books.') # I have {books} books. print(f'I have {books} books.') # I have 3 books. print(F'I\thave\t{books}\tbooks.') # I have 3 books.,Escape 字元都可以用 print(f'I have {books} {{books}}.') # I have 3 {books}. 用 {{ 與 }} Escape { 與 }可以同時使用 f 與 r(Raw 字串),也可以在 f 裡使用續行符號,也就是說除了解析 {變數} 外,就是一般的字串。
print(rf'I\thave\t{books} books.') # I\thave\t3\tbooks.,可以同時使用 r 讓 Escape 字元失效 print(f'I\thave\t{books} \ books.') # I have 3 books.,也可以使用續行符號用 f 標示的字串稱為 f_string,f_string 由三部份組成,不一定都要有。
- 一般字串
- 由大括號組成的 replacement_field
- 用 {{ 與 }} Escape { 與 }
- {
- f_expression
- !conversion:非必要
- :format_spec:非必要
- }
- !
- s 或 r 或 a:分別表示 str()、repr() 與 ascii(),也就是將前面的 f_expression 結果丟到這三個函式裡,再將結果輸出。
goods = 'books' print(f'I have 3 {goods}.') # I have 3 books. print(f'I have 3 {goods!r}.') # I have 3 'books'. print(f'I have 3 {repr(goods)}.') # I have 3 'books'. 結果和 !r 一樣比較令人驚嚇的是在 f_expression 呼叫了函式。
再看 format_spec 組成,這個也很複雜,詳細文件在這,format_spec 不只用在這裡,也可以用在第三個字串格式化 str.format,以及內建函式 format() 裡,這裡先舉簡單的例子,下面在 str.format() 之後也有進一步的研究。
冒號 : 後面表示 format_spec,10 為 width,4 為 precision。
print(f"Result: {12.3456789:10.4}") # Result: 12.35f_expression 可以做的事很多,有些我還看不懂,以下列出我現在了解的。
f_expression 可以使用 list 與 dict。
books = '123' print(f'I have {books[2]} books.') # I have 3 books. d = { 'm': 'Magazine', 'b': 'Book' } print(f'I have 1 {d["m"]}.') # I have 1 Magazine.f_expression 可以呼叫函式。
books = 3 goods = 'books' print(f'I have {books} {goods.upper()}.') # I have 3 BOOKS.f_expression 可以使用巢狀 replacement_field,但只限一層。
print(f"Result: {12.3456789:10.4}") # Result: 12.35 value = 12.3456789 print(f"Result: {value:10.4}") # Result: 12.35 width = 10 precision = 4 print(f"Result: {value:{width}.{precision}}") # Result: 12.35f_expression 可以使用 Conditional expression。
books = 3 print(f'I have {books} {"books" if books > 1 else "book"}.') # I have 3 books. books = 1 print(f'I have {books} {"books" if books > 1 else "book"}.') # I have 1 book.f_expression 不可以直接使用反斜線 \,但可以透過變數傳入。
# print(f'show tab {"\t"}!') # 語法錯誤 t = "\t" print(f'show tab {t}!') # show tab !最後 f_string 不可以作為 docstring,即使它沒有使用 replacement_field。
def foo(): "docstring...." print(foo.__doc__) # docstring.... def bar(): """docstring....""" print(bar.__doc__) # docstring.... def nope(): f"docstring...." print(nope.__doc__) # None
str.format()
官方文件1、官方文件2str 為一般字串與由大括號定義的 replacement fields 組成,replacement fields 可以用 positional 的 index 或者 keyword 的字串定義變數,format() 傳入配合 replacement fields 定義的變數,回傳取代後的新字串。
print('The sum of {0} + {1} is {2}'.format(1, 2, 1 + 2)) # The sum of 1 + 2 is 3 print('My name is {first} {last}'.format(last="Chan", first="Neil")) # My name is Neil Chanstr.format() 與 Formatter class 使用相同的字串格式化語法,與 Formatted string literal 語法近似但不同。
str.format() 字串組成和 Formatted string literal 一樣。
- 一般字串
- 由大括號組成的 replacement_field
- 用 {{ 與 }} Escape { 與 }
- {
- field_name:用 positional 的 index 或者 keyword 的字串定義變數
- !conversion:非必要
- :format_spec:非必要
- }
print('The sum of {} + {} is {}'.format(1, 2, 1 + 2)) # The sum of 1 + 2 is 3取用 list 的 item 與物件的 attribute。
print('The food is {foods[0]}'.format(foods=['banana', 'apple'])) # The food is banana class A: def __init__(self, name): self.name = name def __str__(self): return 'A({self.name})'.format(self=self) print('My name is {0.name}'.format(A('Neil'))) # My name is Neil print(A('Neil')) # A(Neil)取用 dict 要用 ** 炸開。
# print('My name is {0.name}'.format({'name':'Neil'})) # AttributeError # print('My name is {0['name']}'.format({'name':'Neil'})) # 語法錯誤 # print('My name is {0["name"]}'.format({'name':'Neil'})) # KeyError # print('My name is {info["name"]}'.format(info={'name':'Neil'})) # KeyError print('My name is {name}'.format(**{'name':'Neil'})) # My name is Neil也可以用 * 炸開 list。
print('{} {} {}'.format(*'ABC')) # A B Cconversion 用法和 Formatted string literal 不一樣。
- !
- s 或 r 或 a:分別表示 str()、repr() 與 ascii(),也就是將前面的 field_name 結果丟到這三個函式裡,再將結果輸出。
print('My name is {!r}'.format('Neil')) # My name is 'Neil'format_spec 使用方式和 Formatted string literal 完全相同,是最複雜的部份,複雜到可以獨立出來研究。
Format Specification Mini-Language
官方文件format_spec 定義字串的輸出方式,包括寬度、對齊方向、padding 與數值精確度等。
format_spec 可以使用巢狀 replacement field(也就是 field_name、conversion 與 format_spec),但僅限一層。
format_spec 可以用在 Formatted string literal、str.format() 與內建函式 format() 裡。
format_spec 的格式選項可以用在大部份的資料型別,但部份的格式選項僅支援數值型別。
使用空字串作為 format_spec 等於將變數傳入 str() 後的結果帶入字串,其他的格式選項幾乎都會「修改」變數後帶入。
print('My name is {0:}'.format('Neil')) # My name is Neil print('My name is {0!s}'.format('Neil')) # My name is Neil完整的 format_spec 語法如下。
[[fill]align][sign][#][0][width][grouping_option][.precision][type]各項設定說明如下。
- fill:Padding 字元,可以使用任意字元,僅限單一字元,但在 Formatted string literal 與 str.format() 不能使用大括號( { 和 })。
- align:對齊方式,可以使用靠左對齊 <、靠右對齊 >、置中對齊 ^ 與數值型別專用的 =。
- sign:可以使用加號 +、減號 - 與空白字元。
- # 與 0:用在整數與浮點數型別。
- width:整數。
- grouping_option:可以使用底線 _ 與逗號 , 作為千分號。
- precision:整數。
- type:可以使用 b、c、d、e、E、f、F、g、G、n、o、s、x、X、%
type 分成三類
字串(s)、整數(b、c、d、n、o、x、X)與浮點數(e、E、f、F、g、G、n、%)。字串 type
字串 type 只有一個設定值 s,由於只有一個選項,s 可以當作預設值,可以省略不用。print('My name is {0}!'.format('Neil')) # My name is Neil! print('My name is {0:s}!'.format('Neil')) # My name is Neil!,沒有差別由於 type 有三類,字串的預設是 s,整數預設是 d,浮點數預設是 g(類似),當不指定 type 時,如何在 s、d 與 g 中抉擇?很簡單,就是看傳進來的變數是哪一種型別,傳進來的變數是字串就用 s,是整數就用 d,是浮點數就用 g(類似)。
使用 fill、align 與 width 設定對齊方式與 Padding
當 width 大於內容長度時,才會出現 Padding ,換句話說, 當 width 小於內容或沒有指定 width 時,是不會出現 Padding 的,也就是 fill 與 align 就沒有存在的意義了。預設的 Padding 字元為空白,Padding 在數值型別和其他型別有不同的對齊行為,數值型別預設靠右對齊,其他型別預設靠左對齊。
print('My age is {0:10}!'.format(42)) # My age is 42! print('My name is {0:10}!'.format('Neil')) # My name is Neil ! # 10 為 width可以使用 align 來修改對齊行為。
print('My age is {0:<5}!'.format(42)) # My age is 42 ! print('My age is {0:>5}!'.format(42)) # My age is 42! print('My age is {0:^5}!'.format(42)) # My age is 42 ! # <、>、^ 為 align # 5 為 width print('My name is {0:<10}!'.format('Neil')) # My name is Neil ! print('My name is {0:>10}!'.format('Neil')) # My name is Neil! print('My name is {0:^10}!'.format('Neil')) # My name is Neil ! # <、>、^ 為 align # 10 為 width可以加上 Padding 字元(fill),fill 只有在 align 存在下才能使用,換句話說,fill 一定要搭配 align 使用。
print('My name is {0:!<10}!'.format('Neil')) # My name is Neil!!!!!!! print('My name is {0:.>10}!'.format('Neil')) # My name is ......Neil! print('My name is {0:o^10}!'.format('Neil')) # My name is oooNeilooo! # !、.、o 為 fill # <、>、^ 為 align # 10 為 width
數值型別的 sign 與 Padding
sign 為 + 時顯示正負號。print('Max is {0:0=+5} and Min is {1:0=+5}!'.format(4, -3)) # Max is +0004 and Min is -0003! # 0 為 fill # = 為 align,數值型別專用的對齊設定 # + 為 sign # 5 為 widthsign 為 - 時僅顯示負號,不寫正號是常用的寫法,但是正數會比負數多出一個 Padding 字元。
print('Max is {0:0=-5} and Min is {1:0=-5}!'.format(4, -3)) # Max is 00004 and Min is -0003! # 0 為 fill # = 為 align,數值型別專用的對齊設定 # - 為 sign # 5 為 widthsign 可以使用空白字元來拿掉這個多出來的 Padding 字元。
print('Max is {0:0= 5} and Min is {1:0= 5}!'.format(4, -3)) # Max is 0004 and Min is -0003! # 0 為 fill # = 為 align,數值型別專用的對齊設定 # 空白字元為 sign # 5 為 width
數值型別專用的 align =
若對數值型別使用靠右對齊 > 與 Padding 字元,並顯示正負號,結果會嚇死人。print('Max is {0:0>+5} and Min is {1:0>+5}!'.format(4, -3)) # Max is 000+4 and Min is 000-3!應該改用 = 為對齊字元。
print('Max is {0:0=+5} and Min is {1:0=+5}!'.format(4, -3)) # Max is +0004 and Min is -0003!
使用 0 簡易且快速的設定數值型別與浮點數型別 Padding
在不設定 align 的前提下,只要在 width 前面多個 0,就可以達到 fill(0)搭配 align(=)的效果。只用 width 的情況下,Padding 預設為空白字元。
print('Max is {0:5} and Min is {1:5}!'.format(5, -3)) # Max is 5 and Min is -3! print('Max is {0:5} and Min is {1:5}!'.format(5.5, -3.3)) # Max is 5.5 and Min is -3.3!在 width 前面多加一個 0,就可以達成漂亮的 Padding,連正負號的位置都對了。
print('Max is {0:05} and Min is {1:05}!'.format(5, -3)) # Max is 00005 and Min is -0003! print('Max is {0:05} and Min is {1:05}!'.format(5.5, -3.3)) # Max is 005.5 and Min is -03.3!效果如同 fill(0)搭配 align(=)。
print('Max is {0:0=5} and Min is {1:0=5}!'.format(5, -3)) # Max is 00005 and Min is -0003! print('Max is {0:0=5} and Min is {1:0=5}!'.format(5.5, -3.3)) # Max is 005.5 and Min is -03.3!也可以使用 sign 來控制正號顯示與否。
print('Max is {0: 05} and Min is {1: 05}!'.format(5, -3)) # Max is 0005 and Min is -0003! print('Max is {0: 05} and Min is {1: 05}!'.format(5.5, -3.3)) # Max is 05.5 and Min is -03.3!
整數 type 與 #
Python 用 0b 前置字串(零B)表示二進位,例如 0b101 為十進位的 5,用 0o 前置字串(零歐)表示八進位,例如 0o12 為十進位的 10,用 0x 前置字串(零差)表示十六進位,例如 0x12 為十進位的 18。整數 type 預設是 d(十進位),因此不管傳入是 N 進位,在為指定 type 或者指定 type 為 d 時,均會轉成十進位。
print('{0:}'.format(12)) # 12 print('{0:}'.format(0b10)) # 2 print('{0:}'.format(0o12)) # 10 print('{0:}'.format(0x12)) # 18 print('{0:d}'.format(12)) # 12 print('{0:d}'.format(0b10)) # 2 print('{0:d}'.format(0o12)) # 10 print('{0:d}'.format(0x12)) # 18整數 type 可以指定 b(二進位)、o(八進位)、x(小寫的十六進位)與 X(大寫的十六進位)。
print('12 is {0:d} in base 10!'.format(12)) # 12 is 12 in base 10! print('12 is {0:b} in base 2!'.format(12)) # 12 is 1100 in base 2! print('12 is {0:o} in base 8!'.format(12)) # 12 is 14 in base 8! print('12 is {0:x} in base 16!'.format(12)) # 12 is c in base 16! print('12 is {0:X} in base 16!'.format(12)) # 12 is C in base 16!但是直接顯示 1100、14、c 與 C,會造成誤解,這時候可以加上 #,輸出時就會自動產生相對應的前置字串了。
print('12 is {0:#d} in base 10!'.format(12)) # 12 is 12 in base 10! print('12 is {0:#b} in base 2!'.format(12)) # 12 is 0b1100 in base 2! print('12 is {0:#o} in base 8!'.format(12)) # 12 is 0o14 in base 8! print('12 is {0:#x} in base 16!'.format(12)) # 12 is 0xc in base 16! print('12 is {0:#X} in base 16!'.format(12)) # 12 is 0XC in base 16!整數 type 另外有 c(轉成 Unicode 字元,但試不出效果來)與 n(類似 d,但加上千分號,須搭配 Locale 使用)。
整數變數也可以使用下面的浮點數 type,只是會先透過 float() 轉換成浮點數再行套用。
浮點數 type
浮點數可以分成以下三組。- e/E:以指數符號呈現浮點數,precision 預設為 6。
- f/F:以固定長度顯示浮點數,precision 預設為 6。
- g/G:常用格式,依照數值大小以 e/E 或 f/F 顯示,precision 預設為 6。
f = 12345.6789 print('{0:e}'.format(f)) # 1.234568e+04 print('{0:E}'.format(f)) # 1.234568E+04f/F 差別在於 nan(非數值)與 inf(無窮大)的大小寫。
print('{0:f} {0:F}'.format(12345.6789)) # 12345.678900 12345.678900 print('{0:f} {0:F}'.format(float('nan'))) # nan NAN print('{0:f} {0:F}'.format(float('inf'))) # inf INF浮點數另外有 n 與 % 兩種 type,n 請參考下面「為整數或浮點數加上千分號」一節,% 則是用來顯示百分比。
print('{0:%}'.format(0.12)) # 12.000000% print('{0:.2%}'.format(0.12)) # 12.00%
浮點數 type 與 #
當變數的小數位數少於 precision 時,一般會以真實的狀況顯示,也就是少於 precision。print('{0:g}'.format(12)) # 12,precision 預設為 6 print('{0:.4g}'.format(12)) # 12,precision 指定為 4如果有特殊需求,一定要顯示準確的 precision,不足得零時,可以透過 # 達成。
print('{0:#g}'.format(12)) # 12.0000,precision 預設為 6 print('{0:#.4g}'.format(12)) # 12.00,precision 指定為 4浮點數 type g 會移除結尾的零,雖然 # 可以保留結尾的零,但不是原始的狀態。
print('{0:g}'.format(12.30)) # 12.3
precision
precision 只能用在字串 type 與浮點數 type,不能用在整數 type,會報錯(ValueError: Precision not allowed in integer format specifier)。precision 表示顯示的位數,但在 g/G 與 e/E/f/F 有不同的計算方式,在 g/G 是指所有的位數,包括整數與小數,但是在 e/E/f/F 只算小數位數。
print('g/G is {0:.5g}, f/F is {0:.5f}, e/E is {0:.5e}'.format(12.3456)) # g/G is 12.346, f/F is 12.34560, e/E is 1.23456e+01precision 在 g/G 與 e/E/f/F 還有一個明顯的差異,在 g/G 會刪掉結尾的零,不保證顯示的位數,而 f/F 保證顯示的位數,不足的補零。
print('g/G is {0:.5g}, f/F is {0:.5f}, e/E is {0:.5e}'.format(12.34)) # g/G is 12.34, f/F is 12.34000, e/E is 1.23400e+01 print('g/G is {0:.5g}, f/F is {0:.5f}, e/E is {0:.5e}'.format(12.340000)) # g/G is 12.34, f/F is 12.34000, e/E is 1.23400e+01precision 可以用在非數值型別,也就是字串 type,作為「最長字元數」,效果與 width 成對比。
字串比 width 寬時會完整呈現,比 width 窄時會使用 Padding,但是當字串比 precision 寬時會被截掉,比 precision 窄時則完整呈現(不會有 Padding)。
print('My name is {0:5}!'.format('Neil')) # My name is Neil !,Padding print('My name is {0:.5}!'.format('Neil')) # My name is Neil! print('My name is {0:5.5}!'.format('Neil')) # My name is Neil !,Padding print('My name is {0:3}!'.format('Neil')) # My name is Neil! print('My name is {0:.3}!'.format('Neil')) # My name is Nei!,截掉 print('My name is {0:3.3}!'.format('Neil')) # My name is Nei!,截掉當字串同時比 width 窄(會有 Padding)與比 precision 寬時(會被截掉),會發生什麼事?先截掉再 Padding。
print('My name is {0:5.3}!'.format('Neil')) # My name is Nei !
為整數或浮點數加上千分號
有兩種方法,簡單的是只要在 d 或 g 前面加上逗號或底線(誰用底線做千分號?)就可以了。print('{0:,d}'.format(12345)) # 12,345 print('{0:_d}'.format(12345)) # 12_345 print('{0:,g}'.format(1234.5)) # 1,234.5 print('{0:_g}'.format(1234.5)) # 1_234.5比較複雜的是透過 locale 與 n。
print('{0:n}'.format(12345)) # 12345,無效 print('{0:n}'.format(1234.5)) # 1234.5,無效 import locale locale.setlocale(locale.LC_ALL, '') print('{0:n}'.format(12345)) # 12,345,生效 print('{0:n}'.format(1234.5)) # 1,234.5,生效官方文件說底線_可以用在 b、o、x 與 X 的千分號,但實做會報錯(ValueError: Cannot specify ',' or '_' with 'o'.)。
日期格式化
import datetime d = datetime.datetime(2017, 6, 5, 4, 3, 2) print('{:%Y-%m-%d %H:%M:%S}'.format(d)) # 2017-06-05 04:03:02---
---
---
沒有留言:
張貼留言