1.占位符介绍
要实现字符串的拼接,使用占位符是的一种高效、常用的方式。举个例子,下面是不使用占位符的一种写法,直接使用加号拼接字符串
python
name = "Li hua"
age = 24
print("Hello "+name+", you are " + str(age) + " years old")
换成占位符,可以写成
python
name = "Li hua"
age = 24
print("Hello %s, you are %d years old" % (name, age))
其中%s、%d便是占位符,顾名思义,其作用就是替后面的变量站住这个位置,字符串后面的%是一个特殊的操作符,该操作符会将后面的变量值,替换掉前面字符串中的占位符。对比两种写法,会发现使用占位符可以
- 将字符串中用到变量集中在一起,方便查找和修改
- 避免了反复使用引号,导致的引号对应识别困难
- 能够更直接通顺的看出句子的内容
实际上,占位符的优点还有很多,具体可以在下面的使用中去体会。目前常用的占位符写法有三种
- %
- format
- f表达式
每种方法下,占位符的写法和意思又有不同。下面依次介绍下这三种并给出几个使用示例。
2.%
上文已介绍过,%是一个特殊的操作符,该操作符会将后面的变量值,替换掉前面字符串中的占位符。其详细语法格式如下:
python
"... %[key][flags][width][.precision][length type]conversion type ..." % values
其中
python
%[key][flags][width][.precision][length type]conversion type
是该方法下,占位符详细语法的格式。依次介绍下上面占位符每个符号每个字段的意思
%: 必须要有的符号。它标记占位符的开始。key: 选填。映射的键,由带括号的字符序列组成,一般用于后面的values是是字典的场景。flags: 选填。转换标志(Conversion flags), 会影响某些转换类型的结果。width: 选填。最小字段宽度。如果指定为"*"(星号),则实际宽度从值中元组的下一个元素读取,要转换的对象位于最小字段宽度和可选精度之后。precision: 选填。精度,写法为.precision(点+精度)。如果指定为"*"(星号),则实际宽度从值中元组的下一个元素读取,要转换的值位于精度之后。length type: 选填。长度修改器。Conversion type: 必须要有的符号。转换类型,也标记占位符的开始。
下面依次使用一个小示例展示下上面每个字段的用法
Conversion type:由于这个字段是必选字段,所以最先介绍(%写法是固定的,Conversion type则必须要选择一个转换类型)。类型有很多,只介绍三个非常常用的,(更多的建议查阅官方文档:printf-style-string-formatting)
Conversion type |
说明 |
|---|---|
s |
字符串(使用str()方法转换任何Python对象) |
d |
十进制整数 |
f |
十进制浮点数(小数), 自动保留六位小数。 |
示例:
python
print("%s %s %s" % ("hello", 3, 3.1415))
print("%s %d %d" % ("hello", 3, 3.1415))
print("%s %d %f" % ("hello", 3, 3.1415))
print("%s %f %f" % ("hello", 3, 3.1415))
hello 3 3.1415
hello 3 3
hello 3 3.141500
hello 3.000000 3.141500
观察上面的示例,不难看出s是一个非常通用的类型,所以很多不讲究的场景,Conversion type用s是比较可靠的,但是要注意都是字符串类型的。
precision:对于有小数的场景,设置精度是基本操作。其写法为.precision(点+精度)。不设置的话,浮点数默认精度值是6。示例如下:
python
print('%f' % 3.14)
print('%.1f' % 3.14)
print('%.2f' % 3.14)
#保留多少小数
3.140000 #默认精度是6
3.1
3.14
一般来说,%操作符下占位符了解到这里就够了,下面的是比较少用的生僻内容。而且也不实用,复杂的对齐操作推荐使用format或f表达式。
key (不常用):这个选填字段是搭配字典格式的values使用的,示例如下:
python
print("%(name)s %(age)s" %{"name":"zzz","age":20})
print("%(0)s %(1)s" %("zzz",20)) #错误的❌❌❌❌---key必须去字典里取值,这样是不行的
#输出
zzz 20
print("%(0)s %(1)s" %("zzz",20))
TypeError: format requires a mapping
#改正
print("%(0)s %(1)s" %{"0":"zzz", "1":20})
zzz 20
flags :(不常用)该类型可选择的值有:#、0、-、``、+;这里只介绍其中几种,(更多的建议查阅官方文档:printf-style-string-formatting)
flags |
说明 |
|---|---|
0 |
数值的转换将被零填充,需搭配width使用(示例见下面的width中的)。 |
- |
转化结果左对齐,需搭配width使用(示例见下面的width中的), 该标志符会覆盖0标志符。 |
| `` | 空格, 在带符号的转换产生的正数(或空字符串), 之前留一个空格(方便正负数最后对齐)。 |
+ |
如果你在格式化数字时使用了 + 标志,那么正数前面会显示 +负数前面会显示 -而且 + 的优先级比"空格标志"更高,会把它顶掉 |
示例如下
python
print("% d %+d" %(123, 321))
print("%d %+d" %(-123, -321))
#output
123 +321
-123 -321
width:设置字段的最小占位宽度,默认右对齐,内容不够时使用空格填充。
python
print("%4d,%6d,%10f" % (12, 1234, 3.14)) #用至少4个、6个、10个字符显示一个整数,默认右对齐,不够了就用空格补在左边
print("%04d,%06d,%010f" % (12, 1234, 3.14)) #用0来填充不够的位置
print("%-4d,%-6d,%-10f" % (12, 1234, 3.14)) #左对齐
print("%0-4d,%0-6d,%0-10f" % (12, 1234, 3.14)) #左对齐的时候,0是没有意义的,因为会覆盖0
#output
12, 1234, 3.140000
0012,001234,003.140000
12 ,1234 ,3.140000
12 ,1234 ,3.140000
**Python 的字符串格式化里,没有你需要去"使用"的
length type**它是 C 语言 printf 遗留下来的概念,因此在python中我们可以直接忽略它。1.什么是
length?(C 语言里的东西)在 C 语言 里:
cprintf("%ld", x); // long printf("%lld", x); // long long printf("%hd", x); // short这里的:
length 含义 hshort llong lllong long 这是 C 必须要的,因为 C 是弱类型语言。
2.为社么python不需要
length?Python 的整数没有长度限制
pythonx = 10**100 print(x)Python 的
int是 任意精度整数。所以:不存在 "int / long / long long" 的区别。也就不需要
lengthPython 的格式化是"高层抽象"
python"%d" % 10 "%d" % (10**100)都能正常工作。唯一的"历史遗留支持"。Python 的
%格式化里:
python"%ld" % 123 # #l 被 直接忽略 #效果和 %d 一模一样2.
type才是真正重要的东西, **type 决定"怎么解释这个值", **length 在 Python 中不起作用。
type 含义 d十进制整数 f浮点数 s字符串 x十六进制 %百分号
3.format
str.format()是Python2.6开始的新功能,是字符串格式化方法之一,它允许多个替换、值格式化。这个方法允许我们通过位置,格式化连接字符串中的元素。这个方法是一个非常实用且强大的方法。**对于复杂的对齐要求,首选该方法。**其总的语法格式如下:
python
"... {[field_name][!conversion][:format_spec]} ...".format(arguments)
#在 {} 里面可以:
#指定 用哪个值(field_name)---必须要的
#可选地指定 怎么转换这个值(!conversion)----可选的转换符,能够转换为字符换,ASCⅡ转义等。
#可选地指定 怎么格式化显示(:format_spec)
#.format(arguments),Python 字符串的方法,用来 把占位符 {} 替换成实际值
3.1 arguments:首先介绍下arguments,其有两种情况:
- 位置参数(Positional Arguments)-也就是按顺序传递给函数的参数。顺序很重要,如果颠倒就会传错
python
print("{} {}".format("zhang zhang", 668))
print("{1} {0} {0} {1}".format("zhang zhang", 668))
print('{} {} {} {}'.format("zhang zhang", 668)) #位置参数不够,后便只有两个参数就错了
#zhang zhang 668
#668 zhang zhang zhang zhang 668
#IndexError: Replacement index 2 out of range for positional args tuple
- 关键字参数(Keyword Arguments)-也就是通过"名字=值"的方式传递参数。顺序不重要
python
print("{name} {age}".format(name="Li hua", age=24))
print("{name} {age} {age} {name}".format("Li hua", 24))
print("{} {}".format(name="Li hua", age=24))
#Li hua 24
#KeyError: 'name'
#IndexError: Replacement index 0 out of range for positional args tuple
其实位置参数和关键字参数可以混用,但是不推荐
- 混用容易让代码可读性差
- 容易出错(尤其是
{}没索引时只能用位置参数)
然后介绍下该语法下的占位符格式:
python
{[field_name][!conversion][:format_spec]}
(1) field_name: 选填。字段名,常使用其基础格式arg_name来指定使用arguments哪一个。对于关键词参数,arg_name必须为其中的关键字,(此时该字段是必填项)比如"{name} {age}".format(name="Li hua", age=24)。对于位置参数,arg_name必须为序号,(此时该字段可不填,不填则默认第一个为0,从前往后依次+1),比如"{0} {1}".format("Li hua", 24),"{} {}".format("Li hua", 24),两者效果一样。
在
.format()里,field_name就是 占位符{}里指定的内容 ,用来告诉 Python 用哪一个参数去填充这个{}。
- 对于 关键字参数 ,
field_name就是关键字名- 对于 位置参数 ,
field_name就是参数的序号,也可以省略
该字段完整语法格式为arg_name(.attribute_name | [element_index])*,是在arg_name对应的值为对象、列表或字典时使用,获取其进一步的属性值或者内部值。占位符 {} 不只是可以直接对应参数,还可以"深入访问"参数内部的属性或元素。这里举一个例子:
python
#也就是说,arg_name可以访问更深层次的属性
#例如,最基础的 field_name 就是参数名字或位置号
print("{0} {1}".format("Li Hua", 24))
# 0 → "Li Hua"
# 1 → 24
#进一步,还可以访问对象属性。如果参数是对象,可以用点号 .属性名 访问对象属性。
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
p = Person("Li Hua", 24)
print("{0.name} {0.age}".format(p))
#Li Hua 24
#还可以访问列表或者字典元素([element_index]),如果参数是列表、元组或字典,可以用 [] 访问元素。
mylist = ["a", "b", "c"]
print("{0[0]} {0[2]}".format(mylist))
conversion:选填。变换,不常用 。指定时要用!来开头,指定后会在格式化之前将arguments中对应的值进行类型变换。其有三个值可以指定,分别为
conversion |
说明 |
|---|---|
s |
调用结果对象的str方法进行转换 |
r |
调用结果对象的repr方法进行转换 |
a |
调用结果对象的ascii方法进行转换 |
(2)format_spec:选填,格式化具体规范,核心内容,超常用 。填写时要用:来开头,填写后,会按照其指定的规则来进行格式化。其详细语法为
在 Python 的
.format()或 f-string 中,format_spec用来指定 格式化规则 。它总是以 冒号:开头,例如:
pythonpi = 3.14159 f"{pi:.2f}" # 冒号后面就是 format_spec
python
[[fill]align][sign][#][0][width][grouping_option][.precision][type]
其中所有字段均为选填,下面依次介绍下(其中加粗的为常用),
-
fill: 填充内容,如果指定了宽度,但变量长度不够,会使用该字段值进行填充。设置了fill,后面必须显式设置align。pythonf"{5:0>4}" # '0005' # 0 → fill,用 0 来填充 # > → align(右对齐) # 4 → width,最小宽度 4 -
align: 对齐方式,有以下值:
align |
说明 |
|---|---|
< |
强制左对齐(绝大多数对象默认使用) |
> |
强制右对齐(数字类型默认使用) |
= |
强制将填充内容放在符号(如果有)之后但数字之前,比如输出成+000000120这样的格式。此对齐选项仅对数字类型有效。(当'0'紧接在字段宽度width之前时,它将成为默认值。) |
^ |
强制居中对齐 |
sign: 符号展现格式,仅对数字类型有效。有以下值:
sign |
说明 |
|---|---|
+ |
正数负数都显示符号,正数用+,负数用-。无论正负,都必须有符号。 |
- |
(默认值),仅负数展现符号。默认行为,只给负数加符号 |
| `` | 负数展现符号,正数前面使用一个空格来占位对齐。正数用空格占位,负数用 - |
-
#: 复杂生僻,基本不使用,不介绍,有需要的可查阅官方文档(见本部分开头)。#的作用主要有两类:- 进制前缀(二进制 / 八进制 / 十六进制)
- 强制浮点显示小数点
pythonprint(f"{10:#b}") # 0b1010 print(f"{10:#x}") # 0xa print(f"{3.0:#f}") # 3.000000 -
0: 当没有设置对齐方式align时, 在宽度字段前面加一个零('0')字符,将等价于填充字符fill为0且对齐方式align为<。如果你写了
0,但没有写对齐方式Python 会 自动把它理解成:用 0 填充 + 右对齐pythonprint(f"{12:04d}") #0012 -
width: 最小字段宽度,不设置则字段宽度将始终与填充它的数据长度相同(此时对齐方式align没有意义)。width ≠ 固定宽度
至少要占这么多字符不够就补够了就不管
-
grouping_option: 分组选择,有两个选项可选:
grouping_option |
说明 |
|---|---|
, |
表示使用逗号作为千位分隔符。 |
_ |
下划线分隔符,复杂生僻,基本不使用,不介绍. |
pythonprint(f"{123456789:,}") #123,456,789 print(f"{123456789:_}") #123_456_789
-
precision: 精度,指定时要用.来开头,是一个十进制数,指定用'f'和'f'格式化的浮点值在小数点后应该显示多少位,即保留几位小数。precision只能写在.后面pythonf"{3.14159:.2f}" #precision 主要和 `f` 一起用-
.→ 告诉 Python:下面要写"精度" -
2→ 小数点后保留 2 位 -
f→ 浮点数
-
-
type: 类型,决定"怎么显示这个数据"。有很多值,这里只介绍几个常用的:
type |
说明 |
|---|---|
s |
字符串格式。这是字符串的默认类型,可以省略(不填) |
d |
十进制整数 |
f |
十进制浮点数(小数), 默认保留六位小数 |
补充说明1: fill, align只有设置了width才能生效。**fill 和 align 的作用对象是"多出来的宽度空间",如果没有 width,就没有多余空间,所以它们不会生效。**简单示例:
python
#没写的部分 = 使用默认规则,所有规则都是在"width 提供的空间里"起作用
print("{:4}{:6},{:10}".format("1", "2", 3.14)) #只有width,没有align-默认右对齐,没有fill-默认填充空格。
print("{:4}{:>6}, {:^10}".format("1", "2", 3.14)) #有width,
print("{:_<4}{:0>6}, {:^10}".format("1", "2", 3.14)) #有fill。就按照指定的fill填充
#output
1 2 , 3.14
1 2, 3.14
1___000002, 3.14
4.f 表达式
这是从Python 3.6开始的一个新功能。f表达式(f-string), 又名(formatted string literal), 是前缀为"f"或"f", 用花括号{}包裹替换字段的字符串文字。其简易格式为: f'{name} is {age} years old'。其中花括号{}包裹的是替换字段replacement_field,相当于上面的占位符,但是不同于占位符是先占住位置最后标明变量进行替换,f表达式里的替换字段直接在花括号里面进行变量替换 。上面的例子就是用name变量值替换{name}字段,用age变量值替换{age}字段。f表达式详细格式为:
f-string 就是: "在字符串里,用
{}直接写变量或表达式,Python 会自动替换成结果。"只要字符串前面加了f或F,Python 就会:
- 把
{}里面的内容 当成 Python 表达式- 计算结果
- 再变成字符串,放回原来的位置
其中花括号{}包裹的是替换字段 replacement_field。那么和
.format()/%的本质区别是?
python#`.format()`(旧方法) "{0} is {1}".format(name, age) #- `{0}`、`{1}`:先**占位置** #- 后面 `.format(...)` 再**告诉它用哪个值** f-string(新方法) #{name}:直接用变量,没有"对号入座"的过程,所见即所得
python
f'(literal_char | {{ | }} | replacement_field)*'
F'(literal_char | {{ | }} | replacement_field)*'
名称 解释 literal_char普通字符 {``{转义后的左花括号 {}}转义后的右花括号 }replacement_field{变量或表达式}
{}在 f-string 里有 特殊含义 ,如果你只是想输出一个 **字面量(也就是原来写出来的值,些什么就表示本身,不需要再计算。)的{或}**就必须 转义
以上说明f表达式中的字符串内容,是由任意个literal_char、{``{、}}、replacement_field自由组成的。其中literal_char是除花括号{}外的任意字符或空。
f表达式中要表示花括号{}文本,需要进行转义,转义方式为{``{, }},
4.1 replacement_field是替换字段,是f表达式的核心。其格式为
python
{f_expression[=][!conversion][:format_spec]}
-
替换字段由花括号包裹
-
f_expression: 必填内容,常规Python表达式,一般要被圆括号包围,只有少数注意事项:1 不允许使用空表达式。
pythonf"{}" # ❌ 错误2 lambda和赋值表达式
:=必须用显式括号括起来。pythonf"{(x := 10)}"在 f-string 的
{}里:f"{ ... }"里面只能放"能算出一个值的东西",不放那些只做动作不产出结果的东西1:数字
3 #有值,就是 `3`2:变量
x #有值,是 `x` 里存的东西3:计算
x + 1 # 有值,比如 `4`这些都"算得出一个结果" 都可以放进
{},但是像x = 10不是算一个值,他是给x赋值的一个命令。但是:=叫 赋值表达式,它在赋值的同时还返回了这个值(x := 10)把10放进x,然后还返回10;-
大多数表达式 不用括号
-
但有两类表达式 如果不用括号,Python 会"读错"
-
lambdalambda是 Python 的匿名函数,也就是"临时函数",不用给它起名字pythondef add(x, y): return x + y print(add(3, 5)) # 8 #普通函数定义python#如果只是临时用一次,写这么多行很麻烦→ 可以用 lambda 一行搞定: f = lambda x, y: x + y print(f(3, 5)) # 8特性 普通函数 lambda 定义 def name(args): ...lambda args: expression返回 return明确返回自动返回表达式结果 名字 必须有名字 可选(通常匿名,用于临时表达式) 多行 可以多行 只能一行,表达式结果会自动返回 用途 长期使用、复杂函数 临时使用、简单操作 -
:=(赋值表达式,海象运算符)赋值表达式,意思是一边"赋值",一边"返回值"
因为
{}本身不是一个"完整表达式环境" ,而这两种东西语法优先级太特殊,Python 需要你用括号"明确边界"。3 替换表达式可以包含换行符(例如在三重引号字符串中),但不能包含注释。
pythonlong_expr = f"""{ (a + b + c) }""" #Python 会把这个整个括号里的表达式当作一个整体计算 #换行不会破坏计算 #适合复杂计算或长表达式4 每个表达式在格式化字符串文本出现的上下文中按从左到右的顺序进行计算。
pythonx = 2 y = 3 f"{x} + {y} = {x + y}" #先计算第一个 {x} → 2 #再计算第二个 {y} → 3 #再计算第三个 {x + y} → 5 -
其完整格式为:
python
(conditional_expression | * or_expr) (, conditional_expression | , * or_expr)* [,] | yield_expression
过于复杂,只展示不介绍,详情可查阅官方文档:https://docs.python.org/3/reference/lexical_analysis.html#grammar-token-f-expression
-
=: 选填(3.8新版功能), 在表达式后添加等号'=', 可以显示表达式文本及其求值后的值(在调试中很有用),。在 f-string 的替换字段
{}内,加上=,可以打印表达式本身和它的值 ,主要用于调试,方便看变量名字和对应结果pythonx = 5 print(f"{x=}") #x=5 -
conversion: 选填。转换,指定时要在开头添加!,指定后对表达式求值的结果在格式化之前进行转换。在表达式后加
!+ 一个字符,对表达式求值的结果在格式化之前进行转换。pythonname = "Ståle" print(f"{name!s}") # 'Ståle' → str() 结果 print(f"{name!r}") # "'Ståle'" → repr() 结果 print(f"{name!a}") # "'St\\xe5le'" → ascii() 结果 #!s → 普通打印 #!r → 带引号并保留转义字符 #!a → 只保留 ASCII 字符,非 ASCII 转义下方示例中,
name = "Ståle"
conversion |
说明 | 示例 | 输出 |
|---|---|---|---|
s |
对结果调用str()方法 |
f"His name is {name!s}." |
'His name is Ståle.' |
r |
对结果调用repr()方法 |
f"His name is {name!r}." |
"His name is 'Ståle'." |
a |
对结果调用ascii()方法 |
f"His name is {name!a}." |
"His name is 'St\xe5le'." |
format_spec: 格式规范, 和本文第二部分format中的format_spec格式规范是一样的。不过这里的可以嵌套使用replacement_field指定其中的值。也就是需要再冒号后写
python
line = "The output will have the expression text"
print(f"{line = }") #变量名+等号,表示显示变量的值。自动打印 "变量名 = 变量的值"
width = 10
#precision = '4f'
precision = 4
value = 12.34567
print(f"result: {value:{10}.{4}}") #width 为10, precision精度为4,就是保留小数点后4位
print( f"result: {value:{width}.{precision}f}")
#{value:{width}.{precision}}
#{变量:宽度.精度}
#.精度 表示:小数点后保留几位。默认情况下,Python 会四舍五入到指定的精度
#在 f-string 里,当你写 {value:宽度.精度} 且 没有指定类型 时,是按浮点数处理,但是实际上更像%g保留的是有效数字的总位数,并且:精度指的是 小数点后显示的位数,会自动四舍五入。
#当没有指定类型(比如没有写 :10.4f 而是写 :10.4)时,Python 的行为更像 %g(默认类型是g,没有加字母默认是用%g来格式化浮点数,%g 的聪明之处是:自动去除末尾多余的0,尽量用最简洁的方式显示数字。precision = 4 → %g 会先考虑有效数字最多4位):precision = 有效数字的总位数(整数部分 + 小数部分)。
#output
line = 'The output will have the expression text'
result: 12.35
result: 12.3457