# python 建议
# 培养 Pythonic 思维
# f-string 插值格式字符串
格式化操作符%
,常用来格式化字符串,左边的部分是格式化字符串,右边的部分是多个值构成的元组或者字典等。
a = 0b10111011
b = 0xc5f
print('Binary is %d, hex is %d' % (a, b))
2
3
它有四个缺点:
- 如果右侧元组里面的元素位置变动,可能导致左侧的类型对应不上而报错。(或者左侧变动,右侧不变)
- 如果格式化的元素比较多或者需要对某些元素做一些处理,那么整个语句会特别长,甚至展示得很混乱。
- 如果想用同一个值来填充格式字符串里的多个位置,那么必须在右侧的元组中相应地多次重复该值。
- 如果将字典使用到格式字符串中,表达式可能会特别长、混乱。
Python 3 添加了高级字符串格式化(advanced string formatting)机制。把格式有待调整的那些位置在字符串里面先用{}
代替,然后按从左到右的顺序,把需要填写到那些位置的值传给format()
函数,使这些值依次出现在字符串中的相应位置。
key = 'my_var'
value = 1.234
formatted = '{} = {}'.format(key ,value)
print(formatted)
2
3
4
5
如果右侧元组里面的元素位置变动又或者一个值填充多个位置,只需要在右侧{}
中添上对应的左侧参数列表的索引。这样可以解决第 1 和第 3 缺点,但还是解决不了第 2 和第 4 个缺点。
key = 'my_var'
value = 1.234
formatted = '{1} = {0}'.format(key ,value)
print(formatted)
2
3
4
5
Python 3.6 添加了一种新的特性,叫作插值格式字符串(interpolated format string,简称 f-string),可以解决上面提到的所有问题。
key = 'my_var'
value = 1.234
formatted = f'{key} = {value}'
print(formatted)
2
3
4
5
它要求在格式字符串的前面加字母f
作为前缀,而在{}
中直接插入目标值,这样就达到了str.format()
的功能还能简化整个语句。
key = 'my_var'
value = 1.234
c_tuple = '%-10s = %.2f' % (key, value)
args_str = '{:<10} = {:.2f}'.format(key, value)
f_str = f'{key:<10} = {value:.2f}'
c_dict = '%(key)-10s = %(value).2f' % {'key': key, 'value': value}
args_dict = '{key:<10} = {value:.2f}'.format(key=key, value=value)
print(c_tuple == c_dict == f_str)
print(args_str == args_dict == f_str)
2
3
4
5
6
7
8
9
10
11
12
13
# unpacking、enumerate()和 zip()
访问元组里的数据,你能这样写:
item = ('Peanut butter', 'Jelly')
first = item[0]
second = item[1]
print(first, 'and', second)
2
3
4
其实可以不用通过索引来取,可以通过“拆分(unpacking)”对元组进行拆分或者拆包,对元素本身不造成什么影响,只是将元素里面的元素拿出来赋值给其他引用。常用于函数的返回值是一个元组,将数据打包,外界得到这个元组后就可以用 unpacking 拿到对应的变量。
item = ('Peanut butter', 'Jelly')
first, second = item # 拆分
print(first, 'and', second)
2
3
还有一个比较常用的就是数据交换, a, b = <某些表达式>
,会先计算右侧表达式的值,例如表达式5, 8
就会变为元组(5, 8)
,左侧的a, b
就是数据结构拆分(unpacking)了,那么请看下面的数据交换。
a = 5
b = 8
a, b = b, a
print(a, 'and', b)
2
3
4
对于一个包含零食名称和卡路里的零食元组列表,你想把它处理成一个排行榜,你的 for 循环可能会这样写(先不考虑排序):
snacks = [('bacon', 350), ('donut', 240), ('muffin', 190)]
for i in range(len(snacks)):
item = snacks[i]
name = item[0]
calories = item[1]
print(f'#{i+1}: {name} has {calories} calories')
2
3
4
5
6
我们可以使用enumerate()
将这个元组列表处理成带序号的惰性生成器,它生成的每一项都是一个元组,元组的第一项就是序号,元组的第二项就是原始数据的每项值。enumerate(snacks, 1)
就会处理成:
(1, ('bacon', 350))
(2, ('donut', 240))
(3, ('muffin', 190))
2
3
再用 unpacking 对上面的每个元组进行拆分,那么 for 循环就可以改进成:
snacks = [('bacon', 350), ('donut', 240), ('muffin', 190)]
# 第一次拆分将数字标号赋值给rank,第二次拆分是因为snacks的每一项也是元组,拆成name和calories
for rank, (name, calories) in enumerate(snacks):
print(f'#{rank}: {name} has {calories} calories')
2
3
4
你可能还会注意到一点,enumerate()
能很好的取代range()
,它的第二个参数非常好用,如果不带第二个参数,那么序号默认从0
开始。
如果我们想知道一个字符串数组谁的长度更长,你可能会用enumerate()
搭配 unpacking 这样写:
names = ['Cecilia', 'Lise', 'Marie']
counts = [len(n) for n in names]
longest_name = None
max_count = 0
for i, name in enumerate(names):
count = counts[i]
if count > max_count:
longest_name = name
max_count = count
print(longest_name, 'and', max_count)
2
3
4
5
6
7
8
9
10
11
可以使用zip()
,它能将多个迭代序列变成惰性生成器。zip(names, counts)
就会处理成:
('Cecilia', 7)
('Lise', 4)
('Marie', 5)
2
3
那么 for 循环可以优化成
names = ['Cecilia', 'Lise', 'Marie']
counts = [len(n) for n in names]
longest_name = None
max_count = 0
for name, count in zip(names, counts):
if count > max_count:
longest_name = name
max_count = count
print(longest_name, 'and', max_count)
2
3
4
5
6
7
8
9
10
注意:
- 如果提供的迭代器的长度不一致,那么只要其中任何一个迭代完毕,zip 就会停止(按最短的)
- 如果想按最长的那个迭代器来遍历,那就改用内置的 itertools 模块中的 zip_longest 函数
# 令人迷惑的 else
else
是否则的意思,但是for-else
和try-except-else
语句中,这个else
是 and 或者 then 的意思,这是比较令人迷惑的点。
# 例1
for i in range(3):
print('Look', i)
if i == 1:
break
else:
print('Else block!')
# 例2
for i in range(3):
print('Look', i)
else:
print('Else block!')
# 例3
for i in range(0):
print('Look', i)
else:
print('Else block!')
# 例4
flag = True
count = 1
while flag:
if count == 3:
flag = False
count += 1
else:
print('While Else block!')
# 例5
flag = True
count = 1
while flag:
if count == 15: # 这里条件改成5和15依次试试
break
if count == 10:
flag = False
count += 1
else:
print('While Else block!')
# 例6
while False:
print('Never runs')
else:
print('While Else block!')
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
看完上面的例子你应该知道,在“没有进入循环”或者“进入循环后不是由break
终止循环的”两种情况下,才会走到循环的 else 块中。
在循环中没有使用
break
语句时,就没必要使用for-else
或while-else
了。因为循环正常结束(即使循环条件在一开始或者中途变为False
这都是正常结束),也会走到for-else
的else
中,那还不如直接将for-else
的else
语句直接去掉(再把里面的代码去掉一层缩进)。# 上面的例3 for i in range(3): print('Look', i) else: print('1111') # 直接将for的else去掉,并把里面的语句缩进去掉一层。逻辑和效果完全一样 for i in range(3): print('Look', i) print('1111')
1
2
3
4
5
6
7
8
9
10
11循环中使用到了
break
语句,可以使用for-else
或while-else
。循环由break
中断了,那么不会走到循环的 else 语句中;有break
但一直没走到break
,那么就会走到循环的 else 语句中。此时你就知道了,for-else
或while-else
是来对付(应付)break
的两种场景(走没走 break 语句)。for n in range(2, 10): rlt = None for x in range(2, n): if n % x == 0: rlt = x break if rlt: print(n, 'equals', rlt, '*', n//rlt) else: print(n, 'is a prime number') print('--------------------------------') for n in range(2, 10): for x in range(2, n): if n % x == 0: print(n, 'equals', x, '*', n//x) break else: print(n, 'is a prime number')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 列表与字典
← python 进阶 1.用ts编写小爬虫 →