2017-05-25

Python 字串格式化(套用變數)

Python 字串格式化就像是 Hibernate 的 HQL 語法中,用 ? 或者 :name 表示 place holder,稍後可以帶入變數,HQL 的好處是可以改進效能與防止 SQL Injection,Python 字串格式化則是可以簡化靜態字串與變數的串接,並格式化變數。

來到 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 type 是必要,語法與 Format Specification Mini-Language 非常雷同,請參考下方說明。

可以在 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 { 與 }
replacement_field 的語法如下。
  • {
  • f_expression
  • !conversion:非必要
  • :format_spec:非必要
  • }
f_expression 最複雜,留到最後看,先看 conversion 的組成。
  • !
  • 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.35
f_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.35
f_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官方文件2

str 為一般字串與由大括號定義的 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 Chan
str.format() 與 Formatter class 使用相同的字串格式化語法,與 Formatted string literal 語法近似但不同。

str.format() 字串組成和 Formatted string literal 一樣。
  • 一般字串
  • 由大括號組成的 replacement_field
  • 用 {{ 與 }} Escape { 與 }
replacement_field 的語法和 Formatted string literal 有些不一樣,從 f_expression 換成 field_name。
  • {
  • field_name:用 positional 的 index 或者 keyword 的字串定義變數
  • !conversion:非必要
  • :format_spec:非必要
  • }
使用 positional index 時,如果變數依照順序定義,則可以省略 index,直接使用 {}。
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 C
conversion 用法和 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 為 width
sign 為 - 時僅顯示負號,不寫正號是常用的寫法,但是正數會比負數多出一個 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 為 width
sign 可以使用空白字元來拿掉這個多出來的 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。
e/E 差別在於指數符號的大小寫。
f = 12345.6789
print('{0:e}'.format(f)) # 1.234568e+04
print('{0:E}'.format(f)) # 1.234568E+04
f/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+01
precision 在 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+01
precision 可以用在非數值型別,也就是字串 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
---
---
---

沒有留言:

張貼留言