UP | HOME

Python 格式化字符串

目录

1 简介

Python 目前有两种进行格式化字符串的方式:

  • 使用 % 操作符进行格式化字符串
  • 使用 str.format() 方法进行格式化字符串

官方推荐的方式是使用 str.format 方法。

官方文档链接:

2 使用 str.format()

2.1 格式化字符串语法

format 字符串包含由 {} 括起来的替换字段, 不包含在 {} 中的内容不做处理。

format 字符串可以通过 {{}} 的方式来包含 {}.

替换字段语法:

替换字段       - replacement_field ::=  "{" [field_name] ["!" conversion] [":" format_spec] "}"
字段名         - field_name        ::=  arg_name ("." attribute_name | "[" element_index "]")*
参数名         - arg_name          ::=  [identifier | integer]
属性名         - attribute_name    ::=  identifier
元素索引       - element_index     ::=  integer | index_string
索引字符串     - index_string      ::=  <any source character except "]"> +
转换字段       - conversion        ::=  "r" | "s"
格式规范说明符 - format_spec       ::=  <described in the next section>

注意事项:

  • 参数名 - 可以为 整数关键字. 整数为位置参数, 关键字为命名关键字参数。

    如果整数类似于 {0}{1}{2}, 则可以省略, 即: {}{}{} 等价于 {0}{1}{2}.

    如果参数存在属性, 可以通过 arg_name.attribute_name 的形式获取属性值。

    如果参数为可迭代对象, 可以通过 arg_name[integer|index_string] 的形式获取索引位置的元素。

  • 转换字段 - 由 ! 开始, r 代表调用 repr(), s 代表调用 str().
  • 格式规范说明符 - 由 : 开始。

示例:

"First, thou shalt count to {0}"  # 引用第一个位置参数
"Bring me a {}"                   # 隐式引用第一个位置参数
"From {} to {}"                   # 等价于 "From {0} to {1}"
"My quest is {name}"              # 引用关键字参数 "name"
"Weight in tons {0.weight}"       # 第一位置参数的 "weight" 属性
"Units destroyed: {players[0]}"   # 关键字参数 'players' 的第一个元素
"Units destroyed: {players[key]}" # 关键字参数 'players' 'key' 键上的元素
"Harold's a clever {0!s}"         # 首先在参数上调用 str()
"Bring out the holy {name!r}"     # 首先在参数上调用 repr()

2.2 格式说明符

format_spec 允许定义该替换字段的表现形式, 如左右对齐, 宽度等。

标准格式说明符的一般形式:

格式说明符格式 - format_spec ::=  [[fill]align][sign][#][0][width][,][.precision][type]
填充字符       - fill        ::=  <any character>
对齐选项       - align       ::=  "<" | ">" | "=" | "^"
符号选项       - sign        ::=  "+" | "-" | " "
宽度           - width       ::=  integer
精度           - precision   ::=  integer
类型           - type        ::=  "b" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | "n" | "o" | "s" | "x" | "X" | "%"
  • 填充字符 - 如果自定了有效的对齐值, 则可以指定填充字符。 默认为 空格.

    对齐选项:

    Option Meaning
    < 左对齐, 大多数对象的默认值
    > 右对齐, 数字的默认值
    = 强制将填充放置在符号(如果有)之后但位于数字之前, 仅适用于数字参数
    ^ 居中

    示例:

    >>> '{0:?<10}'.format(123456)
    '123456????'
    >>> '{0:?>10}'.format(123456)
    '????123456'
    >>> '{0:?=10}'.format(123456)
    '????123456'
    >>> '{0:?=10}'.format(-123456)
    '-???123456'
    >>> '{0:?^10}'.format(-123456)
    '?-123456??'
    
  • 符号选项 - 符号选项只适用于数字参数。

    符号选项:

    Option Meaning
    + 显示正负数的符号
    - 仅显示负数符号(默认)
    <SPC> 表示在正数前添加空格

    示例:

    >>> '{0:+}'.format(123456)
    '+123456'
    >>> '{0:+}'.format(-123456)
    '-123456'
    >>> '{0:-}'.format(123456)
    '123456'
    >>> '{0:-}'.format(-123456)
    '-123456'
    >>> '{0: }'.format(123456)
    ' 123456'
    >>> '{0: }'.format(-123456)
    '-123456'
    
  • # 选项 - 仅适用于数字参数, 同时仅适用于 2, 8, 16 进制的数字。 会在输出的数字前添加 0b, 0o, 0x 前缀。

    示例:

    >>> '{0:#}'.format(123456)
    '123456'
    >>> '{0:#o}'.format(123456)
    '0o361100'
    >>> '{0:#b}'.format(123456)
    '0b11110001001000000'
    >>> '{0:#x}'.format(123456)
    '0x1e240'
    >>> '{0:o}'.format(123456)
    '361100'
    >>> '{0:b}'.format(123456)
    '11110001001000000'
    >>> '{0:x}'.format(123456)
    '1e240'
    
  • , 选项 - 千位分隔符。

    示例:

    >>> '{0:,}'.format(123456)
    '123,456'
    
  • width 选项 - 指定最小字段宽度, 前面的对齐选项中已经用到了。

    示例:

    >>> '{0:8}'.format(123456)  # 数字默认为 '>'
    '  123456'
    >>> '{0:8}'.format('123456')  # 字符串默认为 '<'
    '123456  '
    
  • 0 选项 - 如果未给出明确的对齐方式, 可以在宽度字段前加上一个 0 字符。 这相当于填充字符 0, 对齐方式为 =.

    示例:

    >>> '{0:08}'.format(-123456)
    '-0123456'
    >>> '{0:8}'.format(-123456)
    ' -123456'
    
  • precision 选项 - 对于由 fF 格式化的浮点数, 该选项指定小数点后的位数。 对于有 gG 格式化的浮点数的小数点前后 一共 多少位。 对于非整数类型的参数, 该选项指定字段最大宽度。 精度不允许使用整数值.

    示例:

    >>> '{0:.5f}'.format(123456)
    '123456.00000'
    >>> '{0:.5g}'.format(123456)
    '1.2346e+05'
    >>> '{0:.5}'.format('123456')
    '12345'
    >>> '{0:.5}'.format(123456)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    ValueError: Precision not allowed in integer format specifier
    
  • type 选项 - 该选项决定了数据应该如何呈现。

    可用的字符串表示类型:

    Type Meaning
    s 字符串格式, 字符串的默认类型, 可省略
    None s

    可用的整数表示类型:

    Type Meaning
    b 输出为 2 进制整数
    c 输出为为相应的 unicode 字符
    d 输出为 10 进制整数
    o 输出为 8 进制整数
    x 输出为 16 进制整数, 使用小写字母
    X 输出为 16 进制整数, 使用大写字母
    n 类似 d, 会使用当前区域设置插入适当的数字分隔符
    None d

    可用的浮点数和小数值表示类型:

    Type Meaning
    e 指数表示法, 使用字母 e 以科学记数法打印数字, 默认进度为 6
    E 类似 e, 使用字母 E
    f 浮点数, 默认进度为 6
    F 浮点数, 同 f
    g 一般形式, 受 precision 选项影响
    G 类似 g, 当使用指数表示法时, 使用 E 而不是 e
    n g 相同, 会使用当前区域设置插入适当的数字分隔符
    % 百分数表示
    None g

3 使用 %

注: 这个方式不被推荐, 如果可以的话, 还是尽量使用 str.format()

这种方式的使用类似于 C 语言中的格式化字符串, 一般形式就是 format % value.

格式:

  1. 字符 %, 标志说明符的开始
  2. 映射键(可选), 有括号阔起来的字符序列指定, 如 %(key)
  3. 转换标志(可选), 影响某些转换类型的结果
  4. 最小字段宽度(可选)。 如果指定为 *, 则实际宽度将从值的下一个元素中读取。并且要转换的值位于宽度之后。

    这在 str.format() 中可以由嵌套的替换字段实现, 如:

    >>> '{0:.{1}f}'.format(123456, 5)
    '123456.00000'
    
  5. 精度(可选)。 以 . 表示。 如果指定为 *, 则实际精度将从值的下一个元素中读取。并且要转换的值位于精度之后。
  6. 长度修饰符(可选)
  7. 转换类型

注: 如果值是一个字典, 那么字符串中的格式必须在 % 字符后面插入一个插入该字典的括号映射键. 如:

>>> print '%(language)s has %(number)03d quote types.' % \
...       {"language": "Python", "number": 2}
Python has 002 quote types.

转换标志:

Flag Meaning
# str.format()
0 在数字前面填充 0 而不是默认的空格
- 左对齐
<SPC> 在正数前面显示空格
+ 在正数前面显示 +

转换类型:

Conversion Meaning
d 格式化整数
i 格式化整数
o 8 进制
u 格式化无符号整型(已过时)
x 16 进制, 小写字母
X 16 进制, 大写字母
e 浮点指数格式, 小写字母
E 浮点指数格式, 大写字母
f 浮点数
F 浮点数
g 浮点数, 如果指数小于 -4 或小于精度, 则使用小写指数格式, 否则使用小数格式
G 浮点数, 类似 g, 使用大写指数
c 单个字符, 格式化字符及其 ASCII 码
r 字符串, 使用 repr() 转换任何 Python 对象
s 字符串, 使用 str() 转换任何 Python 对象
% % 字符

4 Update: 2018-04-18

这次更新是关于使用 str.format 的过程中包含 {} 的问题的。

以前遇到过的一个 Bug, 其中有一串代码大概是这样的:

LOGIN_POST_DATA = r"""
{
    "action": "login",
    "username": "",
    "password": "",
    "ac_id": "2",
    "user_ip": "{user_ip}",
    "nas_ip": "",
    "user_mac": "",
    "save_me": "1",
    "ajax": "1"
}
""".format(user_ip = socket.gethostbyname_ex(socket.gethostname())[-1][0])  # format keyerror

当时偷懒没有用字典, 直接使用了 Json 字符串。

结果就是一运行就出现 Keyerror.

当时一直不明白是什么原因, 后来只能用 replace 来替代 format.

今天, 这个 Bug 的原因搞清楚了 !

Json 啊! 有 {} 啊 !

改一下就好:

LOGIN_POST_DATA = r"""
{{
    "action": "login",
    "username": "",
    "password": "",
    "ac_id": "2",
    "user_ip": "{user_ip}",
    "nas_ip": "",
    "user_mac": "",
    "save_me": "1",
    "ajax": "1"
}}
""".format(user_ip = socket.gethostbyname_ex(socket.gethostname())[-1][0])  # format keyerror

细节问题很重要 !

版权声明:本作品采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可