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


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_name

In [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 18

In [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)
# 其他的我就 不一一测试了。 

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

关于时区

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

关于星期

符号描述
%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分钟
%H24 小时制 时
%I12 小时制 时
%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.14

In [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
# 类型 为 float

print('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 string

In [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=v
    	 print(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 datetime

In [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 Template

In [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 = 10

In [51]: precision = 4

In [55]: value =float("12.34567")

In [56]: value
Out[56]: 12.34567

In [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 = 1024

In [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] += incr
    return 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***********'

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值