TOC

Python 格式化输出

一共四种风格,printf(百分号),format 方法,模板,fstring。

我看有地方把加号也算进来,加号可以说是字符串连接方式,算格式化么?
甚至有地方把 print 传参算进来,无语。

printf-style 字符串格式化

也就是百分号风格。

标记 含义
# -
0 zero padding
- 左对齐
-
+ 正负号
符号 含义
d, i, u int
o int(八进制,octal)
x int(十六进制,hexadecimal)
X int(十六进制,hexadecimal,大写)
e float(指数格式)
E float(指数格式,大写)
f, F float
g e or f
G E or f
c 字符(int, string)
r 字符串(repr)
s 字符串(str)
a 字符串(ascii)
% 百分号

https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting
https://docs.python.org/3/library/stdtypes.html#printf-style-bytes-formatting

"%s" % "hello"
"%s" % ((1, 2), )  # "%s" % a, 如果 a 是元组,就踩坑了
"%(a)s + %(b)s" % {"a": 1, "b": 2}

Format String (format 方法)

https://docs.python.org/3/library/string.html#formatspec

replacement_field ::=  "{" [field_name] ["!" conversion] [":" format_spec] "}"

field_name        ::=  arg_name ("." attribute_name | "[" element_index "]")*
arg_name          ::=  [identifier | digit+]
attribute_name    ::=  identifier
element_index     ::=  digit+ | index_string
index_string      ::=  <any source character except "]"> +

conversion        ::=  "r" | "s" | "a"

format_spec       ::=  [[fill]align][sign][z][#][0][width][grouping_option][.precision][type]
fill              ::=  <any character>
align             ::=  "<" | ">" | "=" | "^"  # 左,右,默认,居中
sign              ::=  "+" | "-" | " "
width             ::=  digit+
grouping_option   ::=  "_" | ","
precision         ::=  digit+
type              ::=  "b" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | "n" | "o" | "s" | "x" | "X" | "%"

conversion 中的 rsa 分别表示 repr,str,ascii。

符号 含义
b 二进制
c 字符
dn 十进制
o 八进制
x 十六进制
X 十六进制,大写

这套语法要强大很多。

"hi, {}!".format("jim")  # hi, jim!
"{2} {2}, {1} {1}, {0} {0}".format('a', 'b', 'c')  # c c, b b, a a
"{a:d} {a:08b}".format(a=7)  # 7 00000111

"{:+10.2f}".format(1 / 3)
# '     +0.33'

"{:-10.2f}".format(1 / 3)
# '      0.33'

'{:.2%}'.format(1 / 3)
# '33.33%'

'{:>10.2%}'.format(1 / 3)
'{:<10.2%}'.format(1 / 3)
'{:=10.2%}'.format(1 / 3)
'{:^10.2%}'.format(1 / 3)  # 居中对齐

'{:x}'.format(11)   # b
'{:#x}'.format(11)  # 0xb
'{:#X}'.format(11)  # 0XB

Template String 模板

https://docs.python.org/3/library/string.html#template-strings

from string import Template
s = Template('$who likes $what')
s.substitute(who='tim', what='kung pao')
# 'tim likes kung pao'

d = dict(who='tim')
Template('Give $who $100').substitute(d)
# Traceback (most recent call last):
# ...
# ValueError: Invalid placeholder in string: line 1, col 11

Template('$who likes $what').substitute(d)
# Traceback (most recent call last):
# ...
# KeyError: 'what'

Template('$who likes $what').safe_substitute(d)
'tim likes $what'

Formatted string literals (f-string / fstring)

Python 3.6 引入。大体上是继承 Format String 语法,但是更加完备,更加方便。
参考:PEP 498, Eric V. Smith, 2015/08

https://docs.python.org/3/reference/lexical_analysis.html#f-strings

f_string          ::=  (literal_char | "{{" | "}}" | replacement_field)*
replacement_field ::=  "{" f_expression ["="] ["!" conversion] [":" format_spec] "}"
f_expression      ::=  (conditional_expression | "*" or_expr)
                         ("," conditional_expression | "," "*" or_expr)* [","]
                       | yield_expression
conversion        ::=  "s" | "r" | "a"
format_spec       ::=  (literal_char | NULL | replacement_field)*
literal_char      ::=  <any code point except "{", "}" or NULL>
name = "Fred"
f"He said his name is {name!r}."
"He said his name is 'Fred'."
f"He said his name is {repr(name)}."  # repr() is equivalent to !r
"He said his name is 'Fred'."

width = 10
precision = 4
value = decimal.Decimal("12.34567")
f"result: {value:{width}.{precision}}"  # nested fields
'result:      12.35'

today = datetime(year=2017, month=1, day=27)
f"{today:%B %d, %Y}"  # using date format specifier
'January 27, 2017'
f"{today=:%B %d, %Y}" # using date format specifier and debugging
'today=January 27, 2017'

number = 1024
f"{number:#0x}"  # using integer format specifier
'0x400'

foo = "bar"
f"{ foo = }" # preserves whitespace
" foo = 'bar'"
line = "The mill's closed"
f"{line = }"
'line = "The mill\'s closed"'
f"{line = :20}"
"line = The mill's closed   "
f"{line = !r:20}"
'line = "The mill\'s closed" '