字符串格式化

我们经常会输出类似“亲爱的xxx你好!你xx月的话费是xx,余额是xx”之类的字符串,而xxx的内容都是根据变量变化的,所以需要一种简便的格式化字符串的方式。

在 Python 中可以使用很多种的字符串格式化方式,下面逐个列举。

字符串格式化的四种方式

一、%号

在Python中,可以采用%来实现字符串格式化,%号格式化字符串的方式是从Python诞生之处就已经存在,时至今日,Python官方也未放弃使用%号,但是也不推荐使用这种格式化方式。

在字符串内部,%s表示用字符串替换,%d表示用整数替换,有几个占位符,后面就跟几个变量或值,顺序要对应。如果只有一个%?,括号可以省略。
如果不太确定应该用什么,可以使用%s,它会把任何数据类型转换为字符串。

有的时候,字符串里的%是一个普通的字符,这个时候就需要转义,用%%来表示一个%。

下面是常见的字符串格式化替代符

%c:格式化字符以及ASCII码

%s:格式化字符串

%d:格式化整数

%u:格式化无符号整数

%o:格式化无符号八进制数

%x:格式化无符号十六进制数

%X:格式化无符号十六进制数(大写)

%f:格式化浮点数,可指定小数点后精度

%e:用科学表示法格式化浮点数

%E:同 %e

%g:%f 和 %e 的简写

%G:%f 和 %E 的简写

Python 数字格式化示例:

数字

格式

输出

描述

3.1415926

{:.2f}

3.14

保留小数点后两位

3.1415926

{:+.2f}

+3.14

带符号保留小数点后两位

-1

{:+.2f}

-1.00

带符号保留小数点后两位

2.71828

{:.0f}

3

不带小数

5

{:0>2d}

05

数字补0(填充左边,宽度为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

指数记法

13

{:>10d}

13

右对齐(默认,宽度为10)

13

{:<10d}

13

右对齐(宽度为10)

13

{:^10d}

13

中间对齐(宽度为10)

11

'{:b}' '{:d}' '{:o}' '{:x}''{:#x}' '{:#X}'

1011 11 13 b0xb 0XB

进制

Python 字符串格式化的代码示例:

a=15        #定义变量a并赋值
b=12345678.1234567    #定义变量b并赋值
strs="I love Python!"    #定义字符串strs并赋值
print("a=%05d"%a)  #使用m控制输出位数,空位补0
print("b=%8.3f"%b)  #输出b,用m.n控制输出的长度和小数点位数
print("%s"%strs)        #正常输出字符串,用于做比较
print("%17s"%strs)    #使用m控制输出字符串长度
print("%17r"%strs)    #使用%r输出字符串
print("%-17.5s"%strs)  #使用-m.n进行左对齐、限制字符串长度和字符位数
print("a={0:05}".format(a))    #输出5位数字空位补0
print("b={0:,.3f}".format(b))    #输出b,用千分位分隔并保留3位小数
print("{0:*^30}".format(strs))  #居中且使用*填充

'''
运行结果:
a=00015
b=12345678.123
I love Python!
   I love Python!
 'I love Python!'
I lov
a=00015
b=12,345,678.123
********I love Python!********
'''

需要注意的一点是,Python中的中文大小为两字符,英文为一字符,所以说在写的时候要格外留意。

二、str.format 格式化

该format方法是在Python2.6中引入的,是字符串类型的内置方法。因为str.format的方式在性能(后面测试了一下,比%要慢)和使用的灵活性上都比%更胜一筹,所以推荐使用。

1. 使用位置参数

# 按照位置一一对应
print('{} asked {} to do something'.format("egon","lili"))
print('{} asked {} to do something'.format("lili","egon"))

2. 使用索引

# 使用索引取对应位置的值
print('{0}{0}{1}{0}'.format('x','y'))

3. 使用关键字参数 or 字典

# 可以通过关键字or字典的方式格式化,打破了位置带来的限制与困扰
# 可以通过关键字or字典方式的方式格式化,打破了位置带来的限制与困扰
print('我的名字是 {name}, 我的年龄是 {age}.'.format(age=18, name='egon'))

kwargs = {'name': 'egon', 'age': 18}
print('我的名字是 {name}, 我的年龄是 {age}.'.format(**kwargs)) # 使用**进行解包操作

关于解包详解可以参看:https://blog.csdn.net/qq_36667170/article/details/121401668

4. 填充和格式化

# 先取到值,然后在冒号后设定填充格式:[填充字符][对齐方式][宽度]

# *<10:左对齐,总共10个字符,不够的用*号填充
print('{0:*<10}'.format('开始执行')) # 开始执行******

# *>10:右对齐,总共10个字符,不够的用*号填充
print('{0:*>10}'.format('开始执行')) # ******开始执行

# *^10:居中显示,总共10个字符,不够的用*号填充
print('{0:*^10}'.format('开始执行')) # ***开始执行***

5. 进度和进制

print('{salary:.3f}'.format(salary=1232132.12351))  #精确到小数点后3位,四舍五入
print('{0:b}'.format(123))  # 转成二进制,结果为:1111011
print('{0:o}'.format(9))  # 转成八进制,结果为:11
print('{0:x}'.format(15))  # 转成十六进制,结果为:f
print('{0:,}'.format(99812939393931))  # 千分位格式化

三、f-Strings 格式化

虽然str.format()比%格式稍微高级了一些,但是它还是有自己的缺陷。当需要传入的字符串过多时,仍然会显得非常冗长。于是在Python3.6中引入了f-strings,不仅比str.format更简洁,性能上也更胜一筹。

f-string是以 f 或 F 开头的字符串,核心在于字符串中符号{}的使用。

1. {} 中可以是变量名

name = 'egon'
age = 18
print(f'{name} {age}')  # egon 18
print(F'{age} {name}')  # 18 egon

2. {} 中可以是表达式

# 可以在{}中放置任意合法的Python表达式,会在运行时计算
# 比如:数学表达式
print(f'{3*3/2}') # 4.5

# 比如:函数的调用
def foo(n):
    print('foo say hello')
    return n

print(f'{foo(10)}') # 会调用foo(10),然后打印其返回值

# 比如:调用对象的方法
name='EGON'
print(f'{name.lower()}') # egon

3. 在类中的使用

class Person(object):
    def __init__(self, name, age):
         self.name = name
         self.age = age
    def __str__(self):
         return f'{self.name}:{self.age}'
    def __repr__(self):
         return f'===>{self.name}:{self.age}<==='

obj=Person('egon',18)
print(obj)                  # 触发__str__
print(obj.__repr__())       # 触发__repr__

# 在f-Strings中的使用
print(f'{obj}')     # 触发__str__
print(f'{obj!r}')   # 触发__repr__

4. 多行 f-Strings

# 当格式化字符串过长时,如下列表info
name = 'Egon'
age = 18
gender = 'male'
hobbie1='play'
hobbie2='music'
hobbie3='read'
info = [f'名字:{name}年龄:{age}性别:{gender}',
        f'第一个爱好:{hobbie1}第二个爱好:{hobbie2}第三个爱好:{hobbie3}'] 

# 我们可以回车分隔到多行,注意每行前都有一个f
info = [
    # 第一个元素
    f'名字:{name}'
    f' 年龄:{age}'
    f' 性别:{gender}',

    # 第二个元素
    f'第一个爱好:{hobbie1}'
    f' 第二个爱好:{hobbie2}'
    f' 第三个爱好:{hobbie3}'
]
print(info)

5. 引号的嵌套

# 当字符串嵌套发送冲突时,与正常的字符串处理方式是一样的
# 1、外层为单引号,内层嵌套也为单引号,并且想要输入的内容也为单引号,那么外层需要改用双引号
print("my name is 'egon'")

# 2、外层为单引号,内层嵌套也为单引号,并且想要输入的内容也为单引号,需要用到转义
print('my name is \'egon\'')

6. 括号的处理

需要注意的是,反斜杠可以用来进行字符转义,但不能用在 {} 的表达式中,注释 # 号也不能出现在 {} 的表达式中。

f'{1\2}' # 语法错误
f'{x#}' # 语法错误

所以当我们的输出的结果中需要包含{}时,类似于之前输出%的做法,需要在原有的基础上再套一层。

print(f'{{天王盖地虎}}') # {天王盖地虎}
print(f'{{{{天王盖地虎}}}}') # {{天王盖地虎}}

四、三种字符串格式化方式的性能对比

from timeit import timeit


def test_s():
    name = 'Egon'
    age = 18
    return '%s:%s.' % (name, age)


def test_format():
    name = 'Egon'
    age = 18
    return '{}:{}.'.format(name, age)


def test_f_strings():
    name = 'Egon'
    age = 18
    return f'{name}:{age}.'


res1 = timeit(test_s, number=1000000)
res2 = timeit(test_format, number=1000000)
res3 = timeit(test_f_strings, number=1000000)
print(res1) # 0.3338866999838501
print(res2) # 0.40734450006857514
print(res3) # 0.24273910000920296 最快

五、标准库模版

从Python2.4起,Python标准库string引入了Template也可以用来格式化字符串,所以说,与前三种方式的一个显著的区别就是:Template属于Python语言的核心语法特征,使用方式如下。

from string import Template

name='EGON'
t = Template('Hello $name!')
res=t.substitute(name=name)

print(res)  # Hello EGON!

还有另外一个不同的地方是,这个模板字符串不支持类似str.format那样的进制转换,需要自己处理

from string import Template

name='EGON'
templ_string = 'Hello $name, there is a $error error!!!'
res=Template(templ_string).substitute(name=name, error=hex(12345))

print(res) # Hello EGON, there is a 0x3039 error!!!

另外稍微吐槽一下,这种方式真的慢

使用模板字符串Template的最佳的时机就是当你的程序需要处理由用户提供的输入内容时。模板字符串是最保险的选择,因为可以降低复杂性。 其他一些复杂的字符串格式化技巧的可能会给你的程序带来安全漏洞。

总结:

  1. 如果格式化的字符串是由用户输入的,那么基于安全性考虑,推荐使用Template

  2. 如果使用的python3.6+版本的解释器,推荐使用f-Stings

  3. 如果要兼容python2.x版本的python解释器,推荐使用str.format

  4. 如果不是测试的代码,不推荐使用%

泥嚎~