文章目录

  • 23.字符串的格式化
  • Resource
    • `format()` 讲解
      • replacement_field 语法说明
      • format_spec 语法说明
        • 符号系统
        • 分组选项(关于整个的语法 如何对应)
        • 精度
      • format 基本使用
        • 关于时间的字符格式化 来回转换
        • 填充与对齐
        • 数字格式化
      • 最简单的 format 理解方式
    • `%` 符号 格式化字符串
      • 一些简单示例
        • 使用字典填充 , 映射键
        • 元组填充 以及转换标志使用
          • %的缺点
          • 实际示例
        • # 的作用
        • 精度使用
        • %转义
      • 语法
        • 动态传入 宽度和精度
      • % 的转换标志(和 # 类似)
      • type 列表(通用,支持类型的格式化方式中通用)
    • 字符串常量
    • 自定义字符串格式(可以定义自己的格式字符串语法)
    • 模板字符串
      • 不使用 Template 模块 构建 模板
      • 使用 `string.Template`
    • F-string 格式化字符串
      • F-string 骚操作
      • 来自python源代码的示例
      • Lib/asyncio/locks.py:
    • 一些示例
      • 按位置访问参数
      • 按名称访问
      • 访问参数的属性
      • 替代 `%s` 和 `%r ` 和 `%a` 和 format 的 conversion 一样
      • 对齐文本指定宽度

23.字符串的格式化

Resource

字符串格式化 % -官方中文文档

字符串常量-string模块-官方中文文档

自定义字符串格式, 可以定义自己的格式字符串语法-官方中文文档

格式字符串语法 format 和 % 的对比 - 官方中文文档

模板字符串 string.Template(template)-官方中文文档

F-string-官方文档

flufl.i18n - 用于国际化的高级 API

时间格式代码 ——1989 C标准所需的所有格式代码的列表,这些代码适用于具有标准C实现的所有平台。比如 cpython

F-STRING - Literal String Interpolation PEP-498


format() 讲解

format 的 优点是 理论上 可以 填充任何字符串
% 则 只能填充 0 和 一些特定的字符 , 根据你 填入的 flag 转换标志来决定

replacement_field 语法说明

如果需要在文本中包含 大括号字符,则可以通过加倍对其进行转义{{{{}}}}.

语法: "{" [field_name] ["!" conversion] [":" format_spec] "}"

replacement_field ::=  "{" [field_name] ["!" conversion] [":" format_spec] "}"
# 主要 有 3 个 可选项field_name        ::=  arg_name ("." attribute_name | "[" element_index "]")*   #  xxx.属性名字 或者   xxx[索引值]
arg_name          ::=  [identifier | digit+] # 标识符 或者 数字
attribute_name    ::=  identifier # 属性名字 为  标识符
element_index     ::=  digit+ | index_string # 数字 或者 是  索引字符串 比如   dict['key']
index_string      ::=  <any source character except "]"> +
conversion        ::=  "r" | "s" | "a"   #  repr  或者 str 或者 ascii 比如 这样写 {!r} {!s} {!a}
format_spec       ::=  <described in the next section> # 查看后面的 详细说明

format_spec 语法说明

{:冒号的右边就是format_spec}

填入了 fill 填充内容的 时候 就 必须 填写 对齐方式 {:0>} 或者 其他 几个 对齐方式<, ^, =

其他的选项都是 可选项

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


format_spec     ::=  [[fill]align] [sign][#][0][width][grouping_option][.precision][type]fill            ::=  <any character> # fill 可以是任何字符的字符,如果省略,则默认为空格。
align           ::=  "<" | ">" | "=" | "^" # 4 个 对齐模式
sign            ::=  "+" | "-" | " "  # 这个 sign 选项仅对数字类型有效 , 为了 表示 有符号数.  简单的说就是 正负
width           ::=  digit+ # 宽度 是  一个 数字
grouping_option ::=  "_" | "," # 分组选项 , 可以理解为  千分位 的 分隔符
precision       ::=  digit+ #  精度 一个数字 也是 和 % 的一样的  .精度   注意这个 点点 嘻嘻
type            ::=  "b" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | "n" | "o" | "s" | "x" | "X" | "%" # 转换类型

这个 ‘#’ 选项使转换使用“替代形式”。

对于不同的类型,替代形式的定义不同。此选项仅对integer、float、complex和decimal类型有效。
后面 % 格式化 字符串 的时候 , 也会 聊到这个 符号.

符号系统

sign 描述
+ 指示正负数字都应使用符号。
- 指示符号只能用于负数(这是默认行为)。
空格 指示应在正数上使用前导空格,在负数上使用减号。

分组选项(关于整个的语法 如何对应)


这个',' 选项表示使用逗号作为千位分隔符。

对于支持区域设置的分隔符,请使用 ‘n’ 改为整数表示类型。

print("{:x=+#025,d}".format(-120000000000000))
-xxxxx120,000,000,000,000 # ####################### 说明 #############################
# 从  冒号 开始 都属于  format_spec 的语法
:  代表  format_spec 开始
x  代表  语法中  的  fill
=  代表 语法中 的 align  也就是  对齐方式
+  代表 sign 也就是说 我们是 要 使用 正号 和 负号的
#  井号是 一个可选项  ‘#’ 选项使转换使用“替代形式”。 此选项仅对integer、float、complex和decimal类型有效, 比如 前缀 0x, 0o,0b 等等
0 代表的是 一个 对齐 方式  0= , 这个的对齐 优先级 没有  fill 和 align 高,  所以 我们的  这个例子, 可以把0 去掉, 结果也是一样的
25 代表  宽度
,  代表 grouping_option 也就是  千分位  分隔符
d  代表 type  类型
# #########################################################
tips: 这个例子 没有 放入 精度
精度就是 在 type 前面  加一个 .数字,
用我们的这个例子来举例 ,
d 类型 没有  精度 , 所以换成 f  浮点数
加了 一个 .2  也就是  精度 为 2
.2 代表的就是  precision  , 这些 都是 结合 上面的语法来看的.
print("{:x=+#025,.2f}".format(-120000000000000))
输出结果: -xx120,000,000,000,000.00# ################# 实验 ##########################
# 我们改成 x 16进制 的转换类型 来看看, 由于 x 类型 不支持 , 分隔符 所以换成 _ 下划线
# 为了 区分 0x 的前缀 我把 填充字符改为了 a
print("{:a=+#025_x}".format(-120000000000000))
输出结果: '-0xaaaaaaaa6d23_ad5f_8000'# 把 #0 去掉 , 发现没有了 0x 前缀
print("{:a=+25_x}".format(-120000000000000))
输出结果: '-aaaaaaaaaa6d23_ad5f_8000'# 去掉 a 字母 填充  和 = 对齐
# 这两个是一起的  fill 和 align  是绑定的
# 也就是  去掉  a=
# 可以看到的是 0x 依然 有 .
# 但是 0x 和 数字中间的 填充 变为了 0
print("{:+#025_x}".format(-120000000000000))
输出结果: '-0x00_0000_6d23_ad5f_8000'# 我们在这个基础上去掉 0
# 由于我们设置的是 25 的长度, 达不到的时候, 会使用 默认的 空白 来填充
# 如果我们取消 井号 那么 0x 也没有了
# 结论是  这个 0  就是 填充 0
print("{:+#25_x}".format(-120000000000000))
输出结果: '        -0x6d23_ad5f_8000'# 那么为了验证 填充 0  和  前面的  fill 和 align  的 优先级
# 经过下面 的 测试, 发现  fill 和 align 的优先级 更高
# 0 的作用是 0= 对齐方式
# 以下 两个 输出一致
print("{:0=+25_x}".format(-120000000000000))
print("{:+025_x}".format(-120000000000000)) 输出结果:
-0000_0000_6d23_ad5f_8000
-0000_0000_6d23_ad5f_8000

这个 ‘_’ 选项表示对浮点数表示类型和 整型 表示类型的 千分位 隔符使用下划线d 对于整数表示类型 ‘b’, ‘o’ , ‘x’ 和 ‘X’ ,下划线将每隔4位插入。

对于其他演示文稿类型,指定此选项是错误的。

print("{:x=+#05_d}".format(-120000000000000))
-120_000_000_000_000

精度

print("{:x=+#5_.8f}".format(-120000000000000))
-120_000_000_000_000.00000000

format 基本使用

Python2.6 开始,新增了一种格式化字符串的函数 str.format(),它增强了字符串格式化的 功能。

基本语法是通过 { }: 来代替以前的 %

format 函数可以接受不限个参数,位置可以不按顺序。

我们通过示例进行格式化的学习。

In [6]: a = "名字是: {0},年龄是: {1}"
# 这里的  0  和 1  就是 上面 语法 中的  arg_nameIn [7]: a.format("onepis",18)
Out[7]: '名字是: onepis,年龄是: 18'In [8]:  a.format("海贼王",6)
Out[8]: '名字是:海贼王,年龄是:6'
# -----------------------------------------------
In [9]: b = "名字是:{0},年龄是{1}。{0}是个好小伙"In [10]: b.format("onepis",18)
Out[10]: '名字是:onepis,年龄是18。onepis是个好小伙'
# -----------------------------------------------
In [11]: c = "名字是{name},年龄是{age}"
# 这里   name 和 age 就是  arg_name
# 如果  name 是 一个字典 , 那么  也可以 使用   name[age]
# 那么 这里 的 name 就是 所谓的  arg_name
# age 就是 index_str
# 如果 传进去的参数 是一个 对象  那么也就可以 使用  .attr 属性名字的方式来访问了.
In [12]: print("name is {data[name]}, age is {data[age]}".format(data={'name':'onepis', 'age':18}))
Out[12]: name is onepis, age is 18In [12]:  c.format(age=19,name='Yaphets')
Out[12]: '名字是Yaphets,年龄是19'
# -----------------------------------------------
In [13]: x=0.999
In [14]: "{0:.3%}".format(x)
'99.900%'In [15]: format(x,'.2%') # 在转换 百分比的时候  不要写 .2f% .2d% 这些都会报错的哈
'99.90%' # .2% 代表两位数的  小数

我们可以通过{索引}/{参数名},直接映射参数值,实现对字符串的格式化,非常方便。

关于时间的字符格式化 来回转换

import datetime
now = datetime.datetime.now
time_tmp = "{:%y/%m/%d %H:%M:%S}"
time_tmp = "{:%D %X}"
time_tmp.format(now())"{:%y/%m/%d}".format(now())
# '21/06/12'
"{:%Y/%m/%d}".format(now())
# '2021/06/12'"{:%I:%M:%S}".format(now())# 12 小时制
"{:%H:%M:%S}".format(now())# 24 小时制"{:%c}".format(now())
# 'Sat Jun 12 04:26:32 2021'"{:%X}".format(now())
# '04:28:36'
"{:%x}".format(now())
# '06/12/21"{:%D}".format(now())
#'06/12/21'"{:%x %X}".format(now())
# '06/12/21 04:31:26'
# 对象 转换回去 也很简单 , 第二个 符号 传递 相应的 符号 即可。
datetime.datetime.strptime("06/12/21", "%x")
# datetime.datetime(2021, 6, 12, 0, 0)
# 其他的我就 不一一测试了。

符号 描述
%p AM PM 上午 下午
%G iso 8601 年, 可以简单的理解为 年份。 如果是 2021年 显示 2021
%g 这个是 比如 2021 年 这里 会显示 21

关于时区

符号 描述
%Z 时区名称 如果 没有的话为 空字符
%z utc 偏移量

关于星期

符号 描述
%u 小写u 表示 数字 表示 的星期几 从1 开始 , 1,2,…, 7
%w 表示 星期几 0 表示 星期天 , 6表示 星期六 0, 1…6
%a 星期几 的缩写, 缩写
%A 星期几 的全称
%U 一年中的 第几周(星期日为一周中的第一天)
%W 一年中的 第几周 (星期一为 一周的第一天)

关于 年

符号 描述
%Y 以世纪为十进制数的年份, 也就是 4 位数的 年份
%y 没有世纪的年份作为零填充十进制数 也就是两位数的年份

关于月

符号 描述
%m 以零填充的十进制数表示的月份。数字
%b 月份的 缩写 英文
%B 月份的 英文全称

关于 日

符号 描述
%d "以零填充的十进制数字表示的月份日期。 当前月份的 第几天 。 数字
%j 一年中的 第几天

关于 时间

符号 描述
%S 秒数
%M 分钟
%H 24 小时制 时
%I 12 小时制 时
%f 微秒

常用日期时间格式简写

符号 描述
%c ’ Sat Jun 12 03:45:22 2021’ 区域设置的 适当的 时间日期 表示
%x 区域设置的 适当的日期表示
%X 区域设置的 适当的 时间表示
%D 日期格式 y/m/d 年/月/日 这个文档中 没有, 我自己 试出来的。其实和 小写 x 一样

日常使用的 完整表示表示

符号 描述
日常使用的 年月日 => 2位数年 “{:%y/%m/%d}”.format(now())
日常使用的 年月日 => 4位数年 “{:%Y/%m/%d}”.format(now())
日常使用的 12小时制 时分秒 “{:%I:%M:%S}”.format(now())
日常使用的 24小时制 时分秒 “{:%H:%M:%S}”.format(now())

填充与对齐

填充常跟对齐一起使用

^、<、> 分别是居中、左对齐、右对齐,后面带宽度


= 强制填充在符号(如果有)之后但在数字之前。这用于以“+000000120”格式打印字段。此对齐选项仅对数字类型有效。当“0”紧跟在字段宽度之前时,它将成为默认值。

(正数和负数等, 可能会有其他我不知道的有符号数字? )

类似于 这种感觉

print("{:x=+#05d}".format(+12))
+xx12
print("{:x=+#05d}".format(-12))
-xx12

: 号后面带填充的字符,只能是一个字符,不指定的话默认是用空格填充

In [13]: "{:*>8}".format("245")
Out[13]: '*****245'In [14]: "我是{0},我喜欢数字{1:*^8}".format("Yamateh","666")# 这里是指用*来填充
Out[14]: '我是Yamateh,我喜欢数字**666***'

数字格式化

浮点数通过 f,整数通过 d 进行需要的格式化。案例如下:

In [15]: a = "我是{0},我的存款有{1:.2f}"In [16]:  a.format("负二代",-9999999999.234342)
Out[16]: '我是负二代,我的存款有-9999999999.23'In [17]: print('i am {0},age:{1:.2f}'.format('tom',3.1415926))
i am tom,age:3.14In [18]: print('i am {0},age:{1}'.format('tom',18))
i am tom,age:18

其他格式,供大家参考:

3.1415926 {:.2f} 3.14 保留小数点后两位
3.1415926 {:+.2f} +3.14 带符号保留小数点后两位
-1 {:+.2f} -1.00 带符号保留小数点后两位
2.71828 {:.0f} 3 不带小数
5 {:0>2d} 05 数字补零 (填充左边, 宽度为2)
5 {:x<4d} 5xxx 数字补x (填充右边, 宽度为4)
10 {:x<4d} 10xx 数字补x (填充右边, 宽度为4)
1000000 {:,} 1,000,000 以逗号分隔的数字格式
0.25 {:.2%} 25.00% 百分比格式
1000000000 {:.2e} 1.00e+09 指数记法
18 {:>10d} ’ 18’ 右对齐 (默认, 宽度为10)
18 {:<10d} '18 ’ 左对齐 (宽度为10)
18 {:^10d} ’ 18 ’ 中间对齐 (宽度为10)

最简单的 format 理解方式

# 以这种 方式 来理解
# 这些名字 我特意 和语法 里面 保持一致
# 非常容易理解
# fill  填充  align 对齐   width 宽度   grouping_option 分割符  precision 精度  type 类型
# 这里 没有 写 0   因为  #0   井号 后面的 这个 0   代表的 其实  是   fill = '0'  align = '='
format_spec = "{:{fill}{align}#{width}{grouping_option}{precision}{type}}"format_spec.format(-120000000000000,fill='x',align='=',width=25,grouping_option='_',precision='.2',type='f',
)# 这里和上面的其实 是 一样的,  但是 我把 值 给了   一个  变量  , 这个变量 在 语法中 叫做  field_name
format_spec = "{val:{fill}{align}#{width}{grouping_option}{precision}{type}}"
format_spec.format(val=-120000000000000,fill='x',align='=',width=25,grouping_option='_',precision='.2',type='f',
)# 要记住 conversion   =>  !r !a !s 这 3 个 是 无法动态传入的.
# 因为 冒号 左侧不允许  有 {} 这个 符号出现
format_spec = ":{fill}{align}{width}{grouping_option}{precision}{type}"
# 变通的方式 是 这样  利用  字符串拼接
# 但是太麻烦了.  一般 也 用不到.
conversion = "!r"
template = f"{{{conversion}{format_spec}}}"# 字符串 格式 不允许使用 = 对齐  以及 #
# 字符串 格式 也不允许 使用 其他类型
# 要记住  # 只能在 数字类型里面使用
template.format('s',fill='x',align='>',width=25,grouping_option='',precision='',type='s',
)

% 符号 格式化字符串

字符串格式化-官方中文文档

一些简单示例

语法: %[映射键][转换标志][最小字段宽度, * ][精度 点 后面是精度][长度修改器] 转换类型

使用字典填充 , 映射键

# %(name)  name  是映射键  s是转换类型
# 最后一个字母就是转换类型
print('name: %(name)s id: %(id)03d !' %{'name': "onepis", "id": 2}) # 使用字典填充name: onepis id: 002 !# -----------------------------------------------

元组填充 以及转换标志使用

转换标志可以给出多个

%的缺点

%无法填充其他的任意字符 , 只有 0 和 空白, 也没有 format 的相对丰富的对齐方式
并且 0 填充只能针对 字符类型. 不能针对 数字
只有 宽度 和 精度 可以 通过 * 动态传入

不过 我们其实 可以 混合 使用, 虽然 大部分的 场景 % 和 format 和 f-string , 以及 Template 都可以适应.
如果还是不行, 那么 几种手段 混合 使用 或者 混合 着 正则 一起 使用

实际示例
# 井号的作用是 使用 16进制等类型的时候 会   0x  ,  0o , 0b 的前缀
'%+-#5x' %99
Out[_79]: '+0x63'# 默认是 左边填充
'%+#5d' %99
Out[_76]: '  +99'
# 右边填充 空白
'%+-#5d' %99
Out[_78]: '+99  '# 空格的作用是 正数的时候 在前面放一个空格, 负数的时候就 使用 负号
'%0 #5d' %99
Out[_80]: ' 0099''%0 #5d' %-99
Out[_81]: '-0099'
# 对于 用0 填充 以及格式为d , 无需使用 # ,除非你有格式需要
# 比如下面这个
# 8进制 的 10 长度 为4  用 0 填充# %0#4o
# 0 和 # 是 转换标志 可以同时给出
# 井号代表 使用 进制 的数字 变成  0x , 0o, 0b 这样的
# 0 代表 对于 数字而言  使用 0 填充
# 4 是 最小字段宽度
# o 是转换类型print('name: %s id: %0#6o !' %("onepis",10))
name: onepis id: 0o0012 !# 0 是 转换标志 , 用 0 填充
# 3 是最小字段宽度
# d 是转换类型
In [22]: print('name: %s id: %03d !' %("onepis",2)) # 使用元组 在 对应位置填充
name: onepis id: 002 ! # 仅仅多出了 一个  井号 转换标志
In [29]: print('name: %s id: %0#3d !' %("onepis",2)) # 可以看到这种情况, 两个方式 输出是一样的.
name: onepis id: 002 !
# 最小字段宽度 变为了 20
In [31]: print('name: %s id: %#20d !' %("onepis",2))
name: onepis id:                    2 !

# 的作用

# --------------------- # 的作用 --------------------------
print('name: %s id: %x !' %("onepis",16))  # 16 进制
name: onepis id: 10 !
print('name: %s id: %#x !' %("onepis",16)) # 输出这个格式的 16进制
name: onepis id: 0x10 !
print('name: %s id: %#0o !' %("onepis",16)) # 8 进制
name: onepis id: 0o20 !
print('name: %s id: %2o !' %("onepis",16)) # 有井号 和 没井号 是 前面符号的区别
name: onepis id: 20 !

精度使用

# -----------------------------------------------
In [32]: print('name: %s id: %.2f !' %("onepis",2))
name: onepis id: 2.00 !
# %.2f
# 转换标志没有
# 精度 为 2
# 类型 为 floatprint('name: %s id: %0#5.2f !' %("onepis",2))
name: onepis id: 02.00 !
# 小数点 也是 占用长度的
# %0#5.2f
# 用 0 填充为 5 的长度, 两位 小数.
# 转换标志  0 和  #
# 最小字段宽度 为 5
# 精度 为 2
# 类型为 float# 可以看到下面两种写法是一样的.
print('name: %s id: %#10.2f !' %("onepis",2))
name: onepis id:       2.00 !
print('name: %s id: %10.2f !' %("onepis",2))
name: onepis id:       2.00 !

%转义

In [33]: x=11
# %% 相当于 转义   输出的就是 % In [34]: "%.2d%%"%(x)
'11%'In [35]: "%.3d%%"%(x)
'011%'In [36]: "%.3f%%"%(x)
'11.000%'In [37]: x=0.11
In [38]: "%.3f%%"%(x*100)
'11.000%'

语法


语法: %[映射键][转换标志][最小字段宽度, * ][精度 点 后面是精度][长度修改器] 转换类型

  1. 这个 ‘%’ 字符,标记说明符的开头。

  2. 映射键(可选),由带括号的字符序列组成(例如, (somename) )

  3. 转换标志(可选),影响某些转换类型的结果。

  4. 最小字段宽度(可选)。如果指定为 ’* ’ (星号),从中的元组的下一个元素读取实际宽度。 价值观 ,要转换的对象在最小字段宽度和可选精度之后。

动态传入 宽度和精度
print('%0*.*f <' %(5,3,2))
# 5 是  宽度
# 3 是 精度
# 2 是 值
2.000 <

这个就表示 长度 为 5 , 精度 为 3 的 一个 浮点数

  1. 精度(可选),作为 ‘.’ (点),然后是精度。如果指定为 ‘*’ (星号),实际精度从中的元组的下一个元素读取。 要转换的值在精度之后。
print('%.*hd <' %(100,2))
# 这里的 100  就 是 放在 * 里面 作为 精度的
  1. 长度修改器(可选)。
    长度修改器 (h , l 或 L )可能存在,但会被忽略,因为这对于Python来说是不必要的——例如。 %ld 相同 %d .

目前我没有发现 有什么作用 , 这个长度修改器

  1. 转换类型。

% 的转换标志(和 # 类似)

下面是 5 个 转换标志

’#'

值转换将使用“替代形式”

’0’

对于数值,转换将被零填充。

-'

转换后的值进行左调整(覆盖 ‘0’ 如果两者都给出,则转换)。
其实 就是填充 从右边 填充了.

’’ 空格

(空格)在有符号转换产生的正数(或空字符串)之前应该留一个空白。

+'

符号字符 (’+’ 或 ‘-’ )将在转换之前(覆盖“空格”标志)。
这个是 处理正负的 标志

覆盖关系 + 会 覆盖 空格
- 会覆盖 0

所以 可能出现的 3 个 在一起的 有效场景是, 最多 3 个一起出现

+-#
0 #

print('%+20d <' %100)
out:                +100 <
print('%+20d <' %-100)
out:                -100 <

type 列表(通用,支持类型的格式化方式中通用)

下面是 转换类型

字符串常量

字符串常量-string模块-官方中文文档

import stringIn [34]: string.ascii_letters # 输出大小写字母
Out[34]: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'In [35]: string.ascii_lowercase  # 输出 小写字母
Out[35]: 'abcdefghijklmnopqrstuvwxyz'In [36]: string.digits # 输出数字
Out[36]: '0123456789'In [38]: string.punctuation # 输出标点符号
Out[38]: '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'# 还有很多 大家 可以去 官网看

自定义字符串格式(可以定义自己的格式字符串语法)

内置的字符串类 str 提供了通过使用 PEP 3101 所描述的 format() 方法进行复杂变量替换和值格式化的能力。 string 模块中的 Formatter 类允许你使用与内置 format() 方法相同的实现来创建并定制你自己的字符串格式化行为。

import string>>> class Person:name="default name"def __init__(self,name=""):self.name=name>>> me=Person("onpis")
>>> data=[me]
​
>>> s="my name is {data[0].name:^30}"
>>> fmt=string.Formatter() # 实例化一个 对象 fmt>>> fmt.format(s,data=data) # string.Formatter().format 函数 , 调用实例对象的 method
'my name is             onpis             '
>>> s="my name is {data[0].name}"
>>> fmt.format(s,data=data)
​'my name is onpis'>>> s="my name is {data[0].name:<10}"
>>> fmt.format(s,data=data)
​'my name is onpis     '
>>> fmt.vformat(s,(),{"data":data}) # vformat 函数
'my name is onpis     '
# 2个方法的结果是完全一样的,只是一个包装了另一个,
# 在 Formatter.vformat方法中必须有4个参数,中间的空元组和最后的空字典必须存在,
# 因为Formatter.vformat的参数不是 (*args, **kwargs) 而是 (format_string, args, kwargs)# 现在继续说上面4个方法的调用顺序,
# get_value调用了get_field,
# get_field调用parse,
# vformat调用了  get_value, 一般情况下我们只需要调用format就足够了,
# 上面的4个方法都是给需要继承Formatter创建自己的格式化语法的时候来覆盖掉的,
# 不过我们可以从这4个方法分析出很多东西。>>> for i,v in enumerate(fmt.parse(s)):
# s="my name is {data[0].name:<10}"
# parse函数 返回 可迭代对象组成的 元组 ('my name is ', 'data[0].name', '<10', None)temp=vprint(temp)>>> fmt.get_field(temp[1],(),{"data":data}) # 调用 get_field 返回 一个元组 ('onpis', 'data')>>> ret = fmt.get_value(fmt.get_field(temp[1], (), {'data':data})[1], (), {'data':data})
>>> ret
[<__main__.Person at 0x1164cb0>] # 这个就和一开始 定义的 data 是 一样的 又回到了 最初

模板字符串

模板字符串-官方中文文档

不使用 Template 模块 构建 模板

其实和 普通的使用 format 没有太大的差别

In [20]: import datetimeIn [21]: template=('\ndate : "{}", Temperature : {:.1f}, Condition :"{}"')In [22]: log=template.format(datetime.datetime.now(),23,17,"good")In [23]: print(log)date : "2020-08-02 01:16:59.545826", Temperature : 23.0, Condition :  "17"

使用 string.Template

$$ 是一种转义 , 本质是 $

这种方式 只能进行一些 简单的 替换 工作

无法使用 填充 .


In [24]: from string import TemplateIn [25]: s = Template('$who likes $what') In [26]:  s.substitute(who='tim', what='kung pao')
Out[26]: 'tim likes kung pao'
# 执行模板替换,返回新字符串。
# safe_substitute 函数
# 当其他异常仍然可能发生时,此方法被称为“safe”,因为它总是尝试返回可用的字符串而不是引发异常。
In [34]: s = Template('$who likes $what') # 也可这样写In [35]: s = Template('${who} likes $what')
In [36]: d=dict(who='tim',what='pp')
In [37]: s.substitute(d)
Out[37]: 'tim likes pp'# 一种偷懒的 参数传递方式
# 只要你 有一个 key  能 对的上  参数的 字典 那么你 就可以 将其 传进去.
dic = {'who':'tim', 'what':'pic'}
s.substitute(**dic)
out: 'tim likes pic'# 属性访问 , 一般来说,不应该更改它,但不强制使用只读访问。s.template     '$who likes $what'
s.substitute(dic,  who='b')
# 添加 关键字参数,  后面添加的  会优先使用.  如果 和  占位符 重复的话, 如果没有命中的话, 则 不会 出现什么.
'b likes pic'

F-string 格式化字符串

我觉得 这个和 format 的那个 语法是 极其 相似的.

A formatted string literal 或 f-string 是前缀为的字符串文本 ‘f’ 或 ‘F’ . 这些字符串可能包含替换字段,替换字段是由大括号分隔的表达式。


解码后,字符串内容的语法为:

在 F-string 中 如果 要使用 { 那么可以 {{ 双倍 快乐 , 就是 转义.

缺点, 必须事先 有变量 .
以及 无法直接在 f-string expression 中使用 反斜杠
以及实在是太灵活了. 看到后面 就 知道了.


这句话非常重要

顶级格式说明符可以包括嵌套替换字段。这些嵌套字段可能包括它们自己的转换字段和 format specifiers ,但可能不包括嵌套更深的替换字段。
因为 可以使用 format 的 语法, 在 replacement_field 中 ,

也就是 说 , 在 {} 里面 可以使用 format 的 小型 语法系统. . replacement_fieldformat_spec 这两个 都可以使用.

而且还多了 f_expression 这个东西

语法: "{" f_expression ["="] ["!" conversion] [":" format_spec] "}"

f_string          ::=  (literal_char | "{{" | "}}" | replacement_field)*# 我们主要还是  看  replacement_field  替换字段
# 3.8 新版功能: 等号 '='
replacement_field ::=  "{" f_expression ["="] ["!" conversion] [":" format_spec] "}"
f_expression      ::=  (conditional_expression | "*" or_expr)("," conditional_expression | "," "*" or_expr)* [","] | yield_expression# 这里的 条件表达式  就是 三元# 可以  not or and 等等# or_expr 是 按位 或 运算# 包括在里面进行循环, 以及函数调用
conversion        ::=  "s" | "r" | "a" # 这个都是 通用的吧  在  这里 讨论的  几个 格式化方式
format_spec       ::=  (literal_char | NULL | replacement_field)* # 这里看到了  这个 是可以递归的.  无线嵌套  真刺激
literal_char      ::=  <any code point except "{", "}" or NULL> # 除了 '{' '}' 或者 NULL 这 3个 其他都是 合法的
yield_atom       ::=  "(" yield_expression ")"
yield_expression ::=  "yield" [expression_list | "from" expression]

In [47]: name = "onepis"In [48]: f"He said his name is {name!r}." #原始字符串 raw
Out[48]: "He said his name is 'onepis'."In [49]: f"He said his name is {repr(name)}."  # repr() is 相当于!r
Out[49]: "He said his name is 'onepis'."# -------------------- 我说的就是 这个 嵌套  ------------------------------
In [50]: width = 10In [51]: precision = 4In [55]: value =float("12.34567")In [56]: value
Out[56]: 12.34567In [57]: f"result: {value:{width}.{precision}}"
# 这个就是 动态 的  传递   精度 和 宽度
Out[57]: 'result:      12.35' # 嵌套字段# 用 % 实现类似效果
# 类似这个效果
# 填充长度 为 6
# 精度 为 3
# 填充字符 为 空格
# 传递的参数 为 2
[ins] In [_i313]: "%#*.*f"%(6,3, 2)
Out[_313]: ' 2.000'
# 填充长度 为 6
# 精度 为 3
# 填充字符 为 0
# 传递的参数 为 2
[ins] In [_i314]: "%0#*.*f"%(6,3, 2)
Out[_314]: '02.000'# 使用 format  进行类似的操作
"{:0>{}.{}f}".format(2,6,3)
# f-string 的话 实际上 是 这样的,   换成变量就可以 动态 传递 这些 参数进去
f"{2:0>{6}.{3}f}"# ------------------- 进制转换 -------------------------------
In [75]: number = 1024In [76]: f"{number:#0x}"
Out[76]: '0x400' # 转换f"{2:0=#18b}"
# 这个是 二进制的 显示
'0b0000000000000010'f"{255:0=#22_b}"
# 对于 16bit  两个 字节长度的 数字 我会 填充 为 这个长度
# 然后 加上 _ 分隔符 看起来方便一些
'0b0_0000_0000_1111_1111'

这些都是 是可以在 f-string 中使用的

format 语法 可以使用的 这里都可以使用 , 包括前面讲的 那些 format_spec 里面那些

3.1415926 {:.2f} 3.14 保留小数点后两位
3.1415926 {:+.2f} +3.14 带符号保留小数点后两位
-1 {:+.2f} -1.00 带符号保留小数点后两位
2.71828 {:.0f} 3 不带小数
5 {:0>2d} 05 数字补零 (填充左边, 宽度为2)
5 {:x<4d} 5xxx 数字补x (填充右边, 宽度为4)
10 {:x<4d} 10xx 数字补x (填充右边, 宽度为4)
1000000 {:,} 1,000,000 以逗号分隔的数字格式
0.25 {:.2%} 25.00% 百分比格式
1000000000 {:.2e} 1.00e+09 指数记法
18 {:>10d} ’ 18’ 右对齐 (默认, 宽度为10)
18 {:<10d} '18 ’ 左对齐 (宽度为10)
18 {:^10d} ’ 18 ’ 中间对齐 (宽度为10)

F-string 骚操作

字符串使用与str.format相同的格式说明符迷你语言。 也就是 我前面说的 那个语法 .

与str.format()类似,可选的格式说明符可能包含在f-string中,

与表达式(或类型转换,如果指定的话)用冒号分隔。如果没有提供格式说明符,则使用空字符串。

允许的转换是'!s', '!r' , '!a'

它们与str.format()中的相同:'!s'调用表达式上的str()
'!r'调用表达式上的repr()

并且'!a'对表达式调用ascii()。在调用format()之前应用这些转换。


表达式不能包含“:”“!”字符串、圆括号、方括号或大括号之外

唯一的例外是!=’运算符是允许的特殊情况。

f ' <text文本> { <expression 表达式> <optional选项 !s, !r, or !a> <optional : format specifier格式说明符> } <text文本> ... '

# 转义 {{f'{{ {4*10} }}'
# 原始字符
# 原生字符
fr'x={4*10}\n'
# 直接调用函数
def foo():return 20
f'result={foo()}'

# 表达式内的Lambda
f'{(lambda x:x*2) (3)}'
# 直接调用函数
def fn(l, incr):result = l[0]l[0] += incrreturn result
lst = [0]
f'{fn(lst,2)} {fn(lst,3)}'
f'{fn(lst,2)} {fn(lst,3)}'
lst
# 直接将 字典 或者  元组 或者 其他 容器  放入其中
msg = ('disk failure', 32)
f'error: {msg}'
# 推导式
# 元组 ,  列表 也是一样的
f'mapping is { {a:b for (a, b) in ((1, 2), (3, 4))} }'
# map配合 lamda  和 列表对导式
f"result:{[i*2 for i in range(10)]},map配合lambda{(list(map(lambda x:x*2,[1,2,3])))}"In [87]: f"result:{[i*2 for i in range(10)]},map配合lambda {(list(map(lambda x:x*2,[1,2,3])))}"
Out[87]: 'result:[0, 2, 4, 6, 8, 10, 12, 14, 16, 18],map配合lambda[2, 4, 6]'
# 三元表达式
[nav] In [_i363]: a=1 ...: b=0 ...: f"{'true' if b else 'false':0>10.5s}"
Out[_363]: '00000false'
# 按位 与 表达式 元组 调用 .__format__()
# 因为 元组 没有 .__format()
# 所以要用 !a  或 !r 或 !s 模式  转换之后 调用  format
f"{2|1,!a:<6}"
Out[_34]: '(3,)  '
# -------------------  一些特殊写法 -------------------------------
In [77]: foo = "bar"
In [78]: f"{ foo = }" # 保留空白  # 需要版本支持  3.8 以上才可以 这样写
Out[78]: "foo ='bar'">>> f"{line = }"  # 需要版本支持  3.8 以上才可以 这样写
'line = "The mill\'s closed"'>>> f"{line = :20}" # 需要版本支持  3.8 以上才可以 这样写
"line = The mill's closed   "
>>> f"{line = !r:20}" # 需要版本支持  3.8 以上才可以 这样写
'line = "The mill\'s closed" '# ------------------- 日期转换 -------------------------------
# tips 这种 操作  format  也是 可以 使用的。
from datetime import datetime
today=datetime.today()today # 输出今天的日期
Out[69]: datetime.datetime(2020, 8, 2, 1, 35, 11, 12516) f"{today:%m %d, %Y}" # 格式化日期
Out[72]: '08 02, 2020' # 关于这个 时间日期  代码 , 链接 我放在了 最前面import datetime
now = datetime.datetime.now
time_tmp = "{:%D %X}"
time_tmp.format(now())f"{now():%D %X}"输出:
'06/12/21 03:15:03'# datetime.strftime(today,'%y/%m/%d %H:%M:%S %A')
# 用 datetime 就比较麻烦了.f"{today:%B %d, %Y}"
Out[73]: 'August 02, 2020'
f"{today:%y/%m/%d %H:%M:%S %A}"                                              │
Out[_51]: '21/05/23 07:57:10 Sunday'      In [74]: f"{today=:%B %d, %Y}" # 3.8 以上才可以使用 海象# -------------------  不支持的特性 -------------------------------
>>> f"newline: {ord('\n')}"  # 报错 ,不允许反斜杠
# 要包含需要反斜杠转义的值,请创建一个临时变量。>>> newline = ord('\n')
>>> f"newline: {newline}"
'newline: 10'

来自python源代码的示例

Lib/asyncio/locks.py:

# 来自python源代码的示例
# Lib/asyncio/locks.py:extra = '{},waiters:{}'.format(extra, len(self._waiters))
extra = f'{extra},waiters:{len(self._waiters)}'# Lib/configparser.py:message.append(" [line {0:2d}]".format(lineno))
message.append(f" [line {lineno:2d}]")# Tools/clinic/clinic.py:methoddef_name = "{}_METHODDEF".format(c_basename.upper())
methoddef_name = f"{c_basename.upper()}_METHODDEF"

一些示例

按位置访问参数

In [1]:  '{0}, {1}, {2}'.format('a', 'b', 'c')
Out[1]: 'a, b, c'In [2]: '{}, {}, {}'.format('a', 'b', 'c')  # 3.1+ only
Out[2]: 'a, b, c'In [3]: '{2}, {1}, {0}'.format('a', 'b', 'c')
Out[3]: 'c, b, a'In [4]: '{2}, {1}, {0}'.format(*'abc')      # unpacking argument sequence 解包 参数序列
Out[4]: 'c, b, a'In [5]: '{0}{1}{0}'.format('abra', 'cad')   # arguments' indices can be repeated 参数的索引可以重复
Out[5]: 'abracadabra'

按名称访问

In [6]: 'Coordinates: {latitude}, {longitude}'.format(latitude='37.24...: N', longitude='-115.81W')
Out[6]: 'Coordinates: 37.24N, -115.81W'In [7]:  coord = {'latitude': '37.24N', 'longitude': '-115.81W'}In [8]: 'Coordinates: {latitude}, {longitude}'.format(**coord) # 字典解包 传递参数
Out[8]: 'Coordinates: 37.24N, -115.81W'

访问参数的属性

In [10]: 'X: {0[0]};  Y: {0[1]}'.format(coord) # 索引访问, 可以在 模板中 访问 传入参数的 索引
Out[10]: 'X: 3;  Y: 5'

替代 %s%r%a 和 format 的 conversion 一样

In [11]: "repr() shows quotes: {!r}; str() doesn't: {!s}".format('test1', 'test2')
Out[11]: "repr() shows quotes: 'test1'; str() doesn't: test2" # {!r} 原生字符 带引号, {!s} 代表字符串 不带引号In [12]: "ascii() show quotes:{!a};".format("tes1") # {!a} 代表 ascii 带有引号
Out[12]: "ascii() show quotes:'tes1';"

对齐文本指定宽度

In [14]: '{:<30}'.format('left aligned')
Out[14]: 'left aligned                  '  #左对齐In [15]: '{:>30}'.format('right aligned') # 右对齐
Out[15]: '                 right aligned'In [16]:  '{:^30}'.format('centered') # 居中对齐
Out[16]: '           centered           'In [17]: '{:*^30}'.format('centered')  # use '*' as a fill char 星号填充 居中对齐
Out[17]: '***********centered***********'

23_字符串的格式化_format 函数_% 占位符 _ f-string _Template _模板字符等相关推荐

  1. Python_基础语法_字符串基本操作__声明方式_占位符_切片_转义字符_加密解密(6)

    目录: 一.字符串介绍 1.字符的渲染 2.字符的编码 3.不可变类型 二.字符串的基本操作 1.字符串声明 2.字符串占位符号 3.字符串索引切片(包含练习) 4.字符串转义 三.字符串的高级操作 ...

  2. python3占位符详解_占位符最新:Python 占位符的使用方法详解_爱安网 LoveAn.com

    关于"占位符"的最新内容 聚合阅读 这篇文章主要介绍了Python 占位符的使用方法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以 ...

  3. mysql 占位符 出错_占位符报语法错误

    新增和更新方法类里面的占位符能够跑起来,但是删除和查询单个女神里面的占位符就会报语法错误,将占位符手动改为具体整型数字就可以正确运行. 以下是部分源码.//删除功能代码块 public void de ...

  4. 3-Tensorflow-demo_02-变量_占位符_feeddict使用

    import tensorflow as tf import numpy as np """ 以线性回归为例 y = x*w +b x = [[1,2,3,],[2,3, ...

  5. Android中的string资源占位符及Plurals string

    Android支持以占位符的方式,定义字符串. 例如,在xml中定义: ......... <string name="subtitle_format">%s crim ...

  6. java string设置编码_详解Java中String类型与默认字符编码

    为什么写这个 至于为什么要写这个,主要是一句mmp一定要讲,绕了一上午,晕死 Java程序中的中文乱码问题一直是一个困扰程序员的难题,自己也不例外,早在做项目时就遇到过很多编码方式的坑,当时想填来着, ...

  7. get占位符传多个参数_未知环境下的Lidar概率占位栅格图(Occupancy Grid Map) Python代码实现...

    自动驾驶Mapping-占位栅格图(Occupancy Grid Map)中介绍了概率占位栅格地图(Probabilistic Occupancy Grid)的原理,并推导了如何利用贝叶斯理论(Bay ...

  8. python格式化输出(二)--字符串的格式化输出

    ** ​字符串的格式化输出 ** 1.使用占位符 (1)三个常用占位符用法 ①d:将整数.浮点数转换成十进制表示.取整,不进行四舍五入. ②f:将整数.浮点数转换成浮点数表示,默认保留小数点后6位,四 ...

  9. Python的打印函数print()中占位符%和format()使用以及f格式化字符串

    目录 一.print中占位符分为3种: 1.% 2.format() 3.f格式化字符串 二.举例说明: 1.%占位符: 2.format()函数: 3.f-字符串格式化: 一.print中占位符分为 ...

最新文章

  1. (转)Silverlight数据校验之INotifyDataErrorInfo
  2. 将不确定变为确定~整形变量是否可以进行位运算(像枚举类型一样)
  3. CoSENT:比Sentence-BERT更有效的句向量方案
  4. zabbix—自动发现端口并监控
  5. Spark transformation算子案例
  6. dll已加载但找不到入口点dllregisterserver_Java 是如何加载类的?
  7. 代码分析工具python_Python代码分析工具:PyChecker、Pylint
  8. 网页中设定表格边框的厚度的属性_请写出在网页中设定表格边框的厚度的属性_______;设定表格单元格之间宽度属性...
  9. 测试开发——flask视图函数与路由 实战重点
  10. 【MySQL】基于MySQL的SQL核心语法实战演练(一)
  11. python 大括号中括号小括号的区分
  12. 清华大学操作系统OS学习(九)——页面置环算法:最优算法、先进先出算法(FIFO)、最近最久未使用算法(LRU)、 CLOCK法、最不常用算法(LFU) 、工作集置换算法、缺页率置环算法
  13. H5游戏《守塔兵团》你必须要知道的4件事
  14. msdtc与oracle,如何解决在Win2003的Oracle链接服务器上MSDTC事务失败问题
  15. C#动态库(dll)查看代码工具(反编译工具)
  16. 利用windbg简单调试dump文件
  17. vios接口虚拟服务器,虚拟VIOS服务器安装手册V1.0.doc
  18. py3_VSCode 配置 Python 环境以及初识 Python 正则表达式
  19. 怎样把两个excel表格合并
  20. 用计算机打女生节快乐,二十好几还没女朋友?女神节的打开方式出了问题

热门文章

  1. 推特php博客,实例代码:使用PHP调用Twitter的RSS
  2. SBUF数据缓冲寄存器
  3. 一款开源的协作文本编辑器
  4. 前端微信一键登录和微信小程序支付总结
  5. 12_微信小程序之微信视频号滚动自动播放视频效果实现
  6. 关于MSP430单片机串口通信数据丢失问题
  7. LA 3406 Bingo *
  8. 两个地理坐标距离计算
  9. 投资案例分析:投资创业公司时,VC 关注什么?
  10. 血型(输血-受血)匹配数电设计