Python字符串格式化方法1

  1. 类C语言格式化
    使用print或printf输出,比较接近C语言的输出方式,基本格式如下:
    "%格式符 %格式符....%格式符" % (对象1, 对象2,....对象n)

类C语言格式化使用%号来分隔左边的字符串模版和右边要被格式化的对象,就像是在左边挖了很多个坑,然后用右边的对象填进去。但是左边的坑有多大,能填什么类型的对象。都是由格式符号来决定的。常见的格式符号有:

  • d,i 带符号的十进制数
  • o 不带符号的八进制数
  • u 不带符号的十进制数
  • x 不带符号的十六进制(小写)
  • X 不带符号的十六进制(大写)
  • e 科学计数法表示的浮点数(小写)
  • E 科学计数法表示的浮点数(大写)
  • f,F 十进制浮点数
  • g 如果指数大于-4或者小于精度值则和e相同,其他情况与f相同
  • G 如果指数大于-4或者小于精度值则和E相同,其他情况与F相
  • c 单字符(接收整数或单字符字符串)
  • r 字符串(使用repr转换任意Python对象)
  • s 字符串(使用str转换任意Python对象)
  • a ASCII字符

看起来很多,但是常用的基本上只有d, i, e, f, s这几个。我们在表里看一下它们的作用是什么,把直接给常用的记住就行,其他特殊类型的格式符号都是在使用的时候才去查询。
看完用法以后,我们来简单看几个示例:

整数和字符串输出

>>> "My name is %s, my age is %d years old" % ('afei', 30)
'My name is afei, my age is 30 years old'

# 浮点数输出
>>> import math
>>> "π 的值是:%f" % (math.pi)
'π 的值是:3.141593'

# 八进制和十六进制输出
>>> "%d translate to oct is %o" % (255, 255)
'255 translate to oct is 377'

>>> "%d translate to hex is %x" % (255, 255)
'255 translate to hex is ff'
# 十六进制大写
>>> "%d translate to hex is %X" % (255, 255)
'255 translate to hex is FF'

# 科学计数法
>>> "%f 对应的科学计数法的值是:%e " % (314.15926, 314.15926)
'314.159260 对应的科学计数法的值是:3.141593e+02 '

# 输出对象
>>> class Demo:
...     def __str__(self):
...             return "This is a demo class"
...     def __repr__(self):
...             return "This is a demo info for machine, not for human"
... 
>>> demo = Demo()
>>> "demo is %s" % (demo)
'demo is This is a demo class'
>>> "demo is %r" % (demo)
'demo is This is a demo info for machine, not for human'

上面的示例中简单的演示了常见的几种格式符号的用法。但是在实际的项目中,还会遇到很多其他的需求,例如
字符串对齐怎么处理

  • 每一列怎么设置宽度
  • 怎么设置小数后面的输出精度

而想要解决这些问题,就需要用到更高级的格式,像下面这样的格式,就是更高级的用法:
"%[(name)][flags][width][.precision] typecode" % (对象,)

这个复杂格式化里面的字段含义如下:

  • (name) 表示根据名称自动解析后面的对象,一般是字典(不需要解包)。
  • flags 转换标志,默认是右对齐,- 表示左对齐,+ 表示在转换值前面加上正负号,0表示转换值宽度不够时用0填充。
  • width 输出字符串的宽度,一般是一个数字表示的值,例如20。
  • precision 设置小数点后有效数字位数,当precision为*号时,由后面的值给出。
  • typecode:即上面列出来的格式符号类型
    同样的,通过示例来演示更为直观:
# 演示name的用法
>>> "My name is %(name)s, and my age is %(age)d" % ({'name':'afei', 'age':30})
'My name is afei, and my age is 30'

# 演示flags和width的用法
# 单个左对齐和15的宽度
>>> dict = {'name':['zhangsan', 'lisi', 'wangwu', 'afei'], 'age':[25, 26, 27, 28, 30]}
>>> for name, age in zip(dict['name'], dict['age']):
...     print("%-15s  %15d" % (name, age))
... 
zhangsan                      25
lisi                          26
wangwu                        27
afei                          28

# 两个元素全部左对齐和15的宽度
>>> for name, age in zip(dict['name'], dict['age']):
...     print("%-15s  %-15d" % (name, age))
... 
zhangsan         25             
lisi             26             
wangwu           27             
afei             28    

# 小数点的用法,3、4、2位小数
>>> "%-15.3f %-15.4f %15.2f" % (1/3, 1/4, 1/6)
'0.333           0.2500                     0.17'
>>> "%-15.*f" % (3, 1234.56789)
'1234.568       '

这里要注意的是:
格式符号和后面的对象类型要匹配,不匹配的时候会出现一些问题,小数点是用来分隔宽度和小数点位数的,别忘了带上。不匹配的时候会出现什么问题呢?来看一下示例:

# 精度丢失
>>> "%-15.3d" % (1/3)
'000            '

# 类型不匹配异常
>>> "%-15.3d" % ('0537')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: %d format: a number is required, not str

这两个问题是最常见的,大家在使用的时候一定要注意。

  1. 格式化调用方法——format方法

因为类C格式化方法的用法和python的哲学并不匹配,Python的哲学是简单,就是要让学习难度更低。于是Python发展出了自己的格式化方法format。format格式化方法使用起来语法相对更加简单,不需要记忆各种格式化符号,基本用法只需要一个花括号就可以了,像下面这样:
"{} {}.... {}".format(对象1, 对象2, ....对象n)

就像是字符串的一个内置方法调用,左边准备格式化的字符串中使用花括号设置待格式化对象的位置,然后format方法里传入对应个数的变量即可。同时,它还支持以下特性
根据format方法的参数位置来指定对象的位置

  • 支持序列解包
  • 支持关键字
  • 支持对象属性访问

下面我们来看一下基本用法的几个示例:

# 位置自动匹配
>>> "My name is {}, age is {}".format('afei', 30)
'My name is afei, age is 30'

# 位置精确匹配
>>> "My name is {1}, age is {0}".format(30, 'afei')
'My name is afei, age is 30'

# 关键字匹配
>>> "My name is {name}, age is {age}".format(age=30, name='afei')
'My name is afei, age is 30'

# 序列解包
>>> "My name is {}, age is {}".format(*['afei', 30])
'My name is afei, age is 30'

# 字典键值对应,这里的字典对象也需要解包后才能匹配,和关键字匹配类似
>>> "My name is {name}, age is {age}".format(**{'name': 'afei', 'age':30})
'My name is afei, age is 30'

# 位置匹配和关键字匹配混用,这里要注意,位置参数要在关键字参数之前
>>> "My name is {name}, age is {0}".format(19, name='afei')
'My name is afei, age is 19'

# 点号访问属性
>>> class Person:
...     def __init__(self, name, age):
...             self.name = name
...             self.age = age
... 
>>> p = Person('afei', 30)
>>> "My name is {0.name}, age is {0.age}".format(p)
'My name is afei, age is 30'

但是在使用过程中,format格式化也会遇到和类C格式化相同的问题,例如对齐、小数位数等。format方法也因此提供了和类C格式化同样的高级用法,高级用法的基本格式是:
{[field_name]!conversion:formatspec}

但是这里面的formatspec又可以划分为更细致的格式化类型,如下所示:
[[fill][align]][sign][#][0][width][separate][.precision][typecode]

也就是说,组合起来,变成了下面这个样子
"{[field_name]![conversion]:[[fill][align]][sign][#][0][width][separate][.precision][typecode]}".format('test')

也就是说,这种格式化方法,从简单的使用上,它确实更简单了,就像上面简单用法里的几个示例一样。但是从功能上说,无疑是变得更强大,也更复杂了。你想实现更高级的定制,就得学习它更高级的用法。我们来看一下它对应的参数怎么使用。
格式参数解释:

  • field_name 两种形式,以0,1,2这样的形式时,表示根据位置来查找变量,以关键字形式时,表示字典格式化,对应字典中的键值。这个在上面简单用法的示例里演示过
  • conversion 表示转换格式,支持三种类型,分别是s|r|a,即str、repr、ascii三种格式的字符串。一般用于类对象的输出,默认是str。
  • fill 填充字符,可以是除'{'、'}'外的任意字符,即当你设置字符串的输出宽度时,空白位置用什么东西来填充。
  • align 字符串对齐方式
    • '<' 左对齐
    • '>' 右对齐
    • '=' 仅用于数字对齐,将填充字符放在数字符号和数字之间,而不是数字符号外面或数字后面。
    • '^' 居中对齐
  • sign 仅针对数字类型
    • '+' 表示正负数都要带上符号
    • '-' 表示只有负数带上符号,正数不需要,默认选项
  • space 表示正数前面要带一个空格,负数前面带'-'号
  • 用来切换不同进制的数字显示形式,十进制没有区别。

  • separate 用来分隔数字的符号,例如使用逗号分隔1,234.5,使用_号分隔,1_234.5
  • width 对齐宽度
  • .precision 输出小数时,数字的有效位数,当后面带上格式符号 f 时,表示小数点后有效位数,如果不带,则表示整个数字的有效位数。
  • typecode 输出的格式,和类C语言格式化里的用法一样,有以下值:
    ·"b" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | "n" | "o" | "s" | "x" | "X" | "%"

看起来格式很多,多练习几次也就熟练了,我们来看一下几个示例:

# 演示conversion用法,-号填充,居中对齐,输出宽度20,
>>> class Demo:
...     def __str__(self):
...             return 'str format'
...     def __repr__(self):
...             return 'REPR FORMAT'
... 
>>> demo = Demo()

# 对象str格式化 
>>> '{!s:-^20}'.format(demo)
'-----str format-----'

# 对象repr格式化
>>> '{!r:-^20}'.format(demo)
'----REPR FORMAT-----'

# 左对齐
>>> '{!s:-<20}'.format(demo)
'str format----------'

# 右对齐
>>> '{!s:->20}'.format(demo)
'----------str format'

# 0填充
>>> '{!s:0>20}'.format(demo)
'0000000000str format'

# =号用于数字对齐,
>>> '{:0>15}'.format(-0.123)   
'000000000-0.123'                # 填充字符在符号数字外面
>>> '{:0=15}'.format(-0.123)
'-0000000000.123'                # 填充字符在负号和数字中间

# 正负数符号示例,这个时候不要和对齐方式混用,会报错。
>>> '{:-f};{:+f}'.format(-0.123, 0.123)
'-0.123000;+0.123000'

>>> '{:-f};{: f}'.format(-0.123, 0.123)
'-0.123000; 0.123000'

# 逗号和下划线分隔符
>>>'{0:<40,.5}'.format(1234.5678)   
'1,234.6                                 '
>>> '{0:<15_.5}'.format(1234.5678) 
'1_234.6        '

# 数字位数
>>> '{:.3f}  {:.4f}'.format(-0.123, 0.123)
'-0.123  0.1230'

# 二进制、八进制、十进制和十六进制输出,没有#号
>>> "{0} 的二进制值是{0:b}".format(100)
'100 的二进制值是1100100'

>>> "{0} 的八进制值是{0:o}".format(100)
'100 的八进制值是144'

>>> "{0} 的十进制值是{0:d}".format(100)
'100 的十进制值是100'

>>> "{0} 的十六进制值是{0:x}".format(100)
'100 的十六进制值是64'

# 二进制、八进制、十进制和十六进制输出,有#号
>>> "{0} 的二进制值是{0:#b}".format(100)
'100 的二进制值是0b1100100'

>>> "{0} 的八进制值是{0:#o}".format(100)
'100 的八进制值是0o144'

>>> "{0} 的十进制值是{0:#d}".format(100)
'100 的十进制值是100'

>>> "{0} 的八进制值是{0:#x}".format(100)
'100 的八进制值是0x64'


# 变量嵌套
>>>'{0:{fill}{align}{width}}'.format('test',fill='-',align='^',width='40')
'------------------test------------------'

以上就是Python格式化最经典的两种用法,下一节的内容会讲解Python 新引入的格式化方法f-string字符串格式化,看一下这种更灵活的字符串格式化方法。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,951评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,606评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,601评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,478评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,565评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,587评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,590评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,337评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,785评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,096评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,273评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,935评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,578评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,199评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,440评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,163评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,133评论 2 352

推荐阅读更多精彩内容