feat(docs): 添加多篇技术博客和竞赛文章
添加快速幂算法详解、蓝桥杯竞赛题解、Python字符串格式化指南等技术文章 新增多张封面图片并调整博客封面布局配置
This commit is contained in:
267
docs/blog/competition/14th-lanqiaocup-python-grad.md
Normal file
267
docs/blog/competition/14th-lanqiaocup-python-grad.md
Normal file
@@ -0,0 +1,267 @@
|
||||
---
|
||||
title: 第十四届蓝桥杯大赛软件赛国赛 Python 研究生组(正在更新)
|
||||
createTime: 2026/01/09 15:57:22
|
||||
cover: /images/elysia/8.jpg
|
||||
coverStyle:
|
||||
layout: right
|
||||
permalink: /archives/b1c77a1d-d402-4788-8049-fa3aeb12ebd0/
|
||||
---
|
||||
## 一、X 质数
|
||||
|
||||
### 题目
|
||||
|
||||
题目链接:0X质数 - 蓝桥云课
|
||||
|
||||
### 问题描述
|
||||
|
||||
对于一个含有 M 个数位的正整数 N ,任意选中其中 K 个不同的数位(0≤K<M),将这些选中的数位删除之后,余下的数位按照原来的顺序组成了一个新的数字 P 。如果至少存在一个 P 是质数,我们就称 N 是一个 X 质数。例如,对于整数 7869 ,我们可以删去 7 和 6 ,得到一个新的数字 89 ,由于 89 是一个质数,因此 7869 是一个 X 质数。又如,对于整数 77 ,可以删去一个 7 后变为质数 7 ,因此 77 也是一个 X 质数。
|
||||
|
||||
请问 1 (含)至 1000000(含)中一共有多少个不同的 X 质数。
|
||||
|
||||
### 解析
|
||||
|
||||
先通过埃氏筛,获取到范围内的所有质数。然后遍历每个数字的所有子串,查看是否满足 X 质数的定义,我们只要找到一个符合条件的子串即可退出循环,只要子串是质数或者 X 质数,我们都可以认定当前数字为 X 质数。
|
||||
|
||||
这里在寻找每个数的子串的时候,可以采用二进制的方式,详情查看:通过位运算快速生成所有的子序列|祀梦的个人博客
|
||||
|
||||
### 答案
|
||||
|
||||
```python
|
||||
# import os
|
||||
# import sys
|
||||
|
||||
# def is_prime(n):
|
||||
# _nums = [True] * (n + 1)
|
||||
# _nums[0] = _nums[1] = False
|
||||
# for i in range(2, n):
|
||||
# if _nums[i]:
|
||||
# for j in range(i ** 2, n + 1, i):
|
||||
# _nums[j] = False
|
||||
# return _nums
|
||||
|
||||
# N = 1000000
|
||||
# nums = is_prime(N)
|
||||
# ans = 0
|
||||
|
||||
# for num in range(N + 1):
|
||||
# digits = [int(n) for n in str(num)]
|
||||
# length = len(str(num))
|
||||
# for mask in range(1,1 << length):
|
||||
# subseq = 0
|
||||
# for i in range(length):
|
||||
# if mask & ( 1 << i ):
|
||||
# subseq = subseq * 10 + digits[i]
|
||||
# if nums[subseq]:
|
||||
# nums[num] = True
|
||||
# ans += 1
|
||||
# break
|
||||
|
||||
# print(ans) # 989457
|
||||
print(989457)
|
||||
```
|
||||
|
||||
## 二、顶板上的正方形
|
||||
|
||||
### 题目
|
||||
|
||||
题目链接:0钉板上的正方形 - 蓝桥云课
|
||||
|
||||
### 问题描述
|
||||
|
||||
小蓝有一个大小为 10×1010×10 钉板,如下图所示,上面等距分布着 10×1010×10 个钉槽(上下/左右相邻的钉槽之间的距离均相同)。其中有一些钉槽上面有钉子, 在图中用红色来进行标注,白色则表示此处没有钉子。小蓝喜欢用橡皮筋在钉 子上围出正方形图案,即选择钉板上的四个钉子,将其作为正方形的四个顶点, 从而把橡皮筋拉伸为一个正方形。
|
||||
|
||||
现在,小蓝想要知道在这个钉板上,可以围出多少种边长不同的正方 形?(相同边长的正方形均视作是同一种,不用考虑正方形的位置)。图中展示 了三种不同边长的正方形,其中蓝色线条表示橡皮筋。
|
||||
|
||||
### 解析
|
||||
|
||||
参考了评论区的大哥(马国良的个人主页 - 蓝桥云课)的思路,如下图所示
|
||||
|
||||
dr 为行之间的差距
|
||||
|
||||
dc 为列之间的差距
|
||||
|
||||
我们遍历 A 和 B,计算 C 和 D 的坐标,分别为 ( x2 + dr , y2 - dc ),( x1 + dr , y1 - dc )
|
||||
|
||||
然后确保CD没有超过棋盘范围并且都有钉子即可
|
||||
|
||||
### 答案
|
||||
|
||||
```python
|
||||
import os
|
||||
import sys
|
||||
from itertools import combinations
|
||||
|
||||
arr = [
|
||||
[1, 1, 0, 1, 0, 1, 1, 1, 1, 1],
|
||||
[1, 1, 1, 0, 0, 1, 1, 1, 1, 0],
|
||||
[1, 1, 0, 0, 1, 0, 1, 1, 1, 1],
|
||||
[1, 0, 1, 1, 0, 1, 1, 1, 1, 0],
|
||||
[1, 0, 1, 0, 1, 1, 1, 1, 0, 0],
|
||||
[1, 0, 0, 1, 0, 1, 0, 1, 0, 1],
|
||||
[1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
|
||||
[0, 1, 1, 1, 1, 1, 1, 1, 1, 0],
|
||||
[0, 1, 1, 0, 1, 0, 1, 1, 1, 1],
|
||||
[1, 0, 1, 0, 0, 1, 0, 1, 0, 0]
|
||||
]
|
||||
points = [(i, j) for i in range(10) for j in range(10) if arr[i][j]]
|
||||
squares = set()
|
||||
|
||||
for (x1, y1), (x2, y2) in combinations(points, 2):
|
||||
dx, dy = x1 - x2, y1 - y2
|
||||
x3 = x2 + dy
|
||||
y3 = y2 - dx
|
||||
x4 = x1 + dy
|
||||
y4 = y1 - dx
|
||||
if all(0 <= coord < 10 for coord in [x3, y3, x4, y4]):
|
||||
if arr[x3][y3] and arr[x4][y4]:
|
||||
squares.add(dx*dx + dy*dy)
|
||||
print(len(squares))
|
||||
```
|
||||
|
||||
## 三、整数变换
|
||||
|
||||
### 题目
|
||||
|
||||
题目链接:0整数变换 - 蓝桥云课
|
||||
|
||||
### 问题描述
|
||||
|
||||
小蓝有一个整数 n。每分钟,小蓝的数都会发生变化,变为上一分钟的数减去上一分钟的数的各个数位和。
|
||||
|
||||
例如,如果小蓝开始时的数为 23,则下一分钟变为 23−(2+3)=18,再下一分钟变为 18−(1+8)=9,再下一分钟变为 9−9=0,共经过了 3 分钟变为 0。
|
||||
|
||||
给定一个正整数,请问这个数多少分钟后变为 0。
|
||||
|
||||
### 解析
|
||||
|
||||
这题直接暴力就过了。
|
||||
|
||||
Python3 跑出来只能通过 60% 的测试用例,用 PyPy3 就好了。
|
||||
|
||||
### 答案
|
||||
|
||||
```python
|
||||
import os
|
||||
import sys
|
||||
|
||||
num = int(input())
|
||||
count = 0
|
||||
while num != 0:
|
||||
num -= sum(map(int,str(num)))
|
||||
count += 1
|
||||
print(count)
|
||||
```
|
||||
|
||||
### 整数变换的第二种做法
|
||||
|
||||
### 答案
|
||||
|
||||
```python
|
||||
import os
|
||||
import sys
|
||||
|
||||
# 对 1000 以内的数进行预处理
|
||||
# difference[i][j] 代表每次减去 sum_digits(i) + j 之后,剩余的值,可能为负数
|
||||
# step[i][j] 记录最终结果
|
||||
# 题目中数字的最大值范围为 10的9 次方,我们每一步多减去的 j 值代表的其实是前六位的和
|
||||
# j 最大也就是 54( 前六位都是9 )
|
||||
|
||||
difference = [[0 for _ in range(55)] for _ in range(1000)]
|
||||
step = [[0 for _ in range(55)] for _ in range(1000)]
|
||||
|
||||
# 这里要考虑一下边界情况,有可能后三位为 0 前六位还有
|
||||
# 这个时候要减去前六位的值
|
||||
# 并且步数需要加一步
|
||||
difference[0] = [-num for num in range(55)]
|
||||
step[0] = [0] + [1 for num in range(54)]
|
||||
|
||||
# 先计算出 0 ~ 999 以内的数的值
|
||||
for i in range(1,1000):
|
||||
# 遍历所有可能的 j 值
|
||||
for j in range(55):
|
||||
ans,num = 0,i
|
||||
while num > 0:
|
||||
num -= ( sum(map(int,str(num))) + j )
|
||||
ans += 1
|
||||
difference[i][j] = num
|
||||
step[i][j] = ans
|
||||
|
||||
# 当 num 小于 1000 的时候直接返回
|
||||
num = int(input())
|
||||
if num < 1000:
|
||||
print(step[num][0])
|
||||
else:
|
||||
ans = 0
|
||||
while num:
|
||||
if num < 1000:
|
||||
ans += step[num][0]
|
||||
break
|
||||
# x 为前六位
|
||||
# y 为后三位
|
||||
x = num // 1000
|
||||
y = num % 1000
|
||||
# 直接加上步数,以及溢出的值即可
|
||||
ans += step[y][sum(map(int,str(x)))]
|
||||
num = x * 1000 + difference[y][sum(map(int,str(x)))]
|
||||
print(ans)
|
||||
```
|
||||
|
||||
## 四、火车运输
|
||||
|
||||
## 五、最大区间
|
||||
|
||||
### 题目
|
||||
|
||||
题目链接:0最大区间 - 蓝桥云课
|
||||
|
||||
### 问题描述
|
||||
|
||||
给定一个长度为 n 的序列 Ai,求 L,R 使 (R−L+1)⋅min(AL,AL+1,…,AR) 尽可能大,其中 min 表示最小值。
|
||||
|
||||
你只需要输出最大的值即可,不需要输出具体的 L,R。
|
||||
|
||||
### 解析
|
||||
|
||||
这道题要求给定字符串中的 (R−L+1)⋅min(AL,AL+1,…,AR) 的最大值,每次计算区间内的最小值再扩展区间很麻烦,我们可以转换一下思路以当前元素为区间内的最小元素,视为右边界,然后不断向左边扩展,一直到小于当前元素的位置停下,就可以得到当前区间的结果。(这里为什么不用向右边扩展,因为如果存在这种情况的话,右边向左扩展的时候会包括这一段)我们可以采用单调递增栈来加快寻找的速度,0 是为了确保清空栈。
|
||||
|
||||
可以用下面这个例子 debug 一下程序查看运行过程。
|
||||
|
||||
n = 6
|
||||
|
||||
A = [1,2,3,2,3,2]
|
||||
|
||||
(感觉解释的还是不太够清楚,有啥问题可以直接评论)
|
||||
|
||||
### 答案
|
||||
|
||||
```python
|
||||
import os
|
||||
import sys
|
||||
|
||||
n = int(input())
|
||||
A = list(map(int,input().split()))
|
||||
A.append(0)
|
||||
|
||||
stack = []
|
||||
ans = 0
|
||||
|
||||
for i in range(n+1):
|
||||
while stack and A[i] <= A[stack[-1]]:
|
||||
prev_i = stack.pop()
|
||||
L = stack[-1] if stack else -1
|
||||
ans = max( ( i - L - 1 ) * A[prev_i] ,ans)
|
||||
stack.append(i)
|
||||
|
||||
print(ans)
|
||||
```
|
||||
|
||||
## 六、等腰三角形
|
||||
|
||||
## 七、连续数组
|
||||
|
||||
## 八、质数排序
|
||||
|
||||
## 九、选段排序
|
||||
|
||||
## 十、最长同类子串
|
||||
|
||||
78
docs/blog/technology/fast-power-algorithm.md
Normal file
78
docs/blog/technology/fast-power-algorithm.md
Normal file
@@ -0,0 +1,78 @@
|
||||
---
|
||||
title: 快速幂算法详解
|
||||
createTime: 2026/01/09 16:05:00
|
||||
cover: /images/elysia/9.jpg
|
||||
coverStyle:
|
||||
layout: left
|
||||
permalink: /archives/1325a3bf-91d7-43ff-9630-e894549e12c1/
|
||||
---
|
||||
## 简介
|
||||
|
||||
快速幂算法是用于快速计算的算法,可以用于快速的处理大整数幂的场景。
|
||||
|
||||
最简单的 for 循环求数字的 n 次幂需要 $O(n)$ 的时间复杂度。快速幂方法可以达到 $O(\log N)$ 的时间复杂度。
|
||||
|
||||
快速幂的核心思想是将指数拆分,以达到快速计算的目的。
|
||||
|
||||
## 快速幂 - 二进制法
|
||||
|
||||
### 原理
|
||||
|
||||
二进制法的核心思想是将指数转换为二进制形式,通过逐位处理并结合平方运算减少乘法次数。
|
||||
|
||||
**二进制分解指数**
|
||||
将指数 $n$ 表示为二进制形式,例如 $n=13$ 对应二进制为 `1101`,即 $13=8+4+1=2^3+2^2+2^0$。
|
||||
|
||||
**幂的拆分**
|
||||
根据二进制分解,$a^n=a^{2^k} \times a^{2^{k-1}} \times \dots \times a^{2^0}$,其中仅当二进制位为 1 时,对应项被保留。
|
||||
|
||||
**逐位处理与平方加速**
|
||||
- 从最低位到最高位依次处理二进制每一位。
|
||||
- 若当前位为 1,则将当前的底数累乘到结果中。
|
||||
- 每一步将底数平方,为处理更高位做准备。
|
||||
|
||||
### 代码示例
|
||||
|
||||
```python
|
||||
def power(base, exp):
|
||||
res = 1
|
||||
while exp > 0:
|
||||
if exp & 1:
|
||||
res *= base
|
||||
base *= base
|
||||
exp >>= 1
|
||||
return res
|
||||
```
|
||||
|
||||
## 快速幂 - 折半法
|
||||
|
||||
### 原理
|
||||
|
||||
折半法的核心公式如下:
|
||||
|
||||
我们要快速计算 $a$ 的 $n$ 次方:
|
||||
|
||||
- 当 $n$ 为奇数的时候:$a^n = a \cdot (a^2)^{(n-1)/2}$
|
||||
- 当 $n$ 为偶数的时候:$a^n = (a^2)^{n/2}$
|
||||
- 当 $n$ 为 0 的时候,直接返回 1
|
||||
|
||||
通过递归或迭代,将大指数问题分解为小指数问题,最后合并结果。
|
||||
|
||||
### 代码示例
|
||||
|
||||
```python
|
||||
def power(base, exp):
|
||||
if exp == 0: return 1
|
||||
half = power(base, exp // 2)
|
||||
return half * half * (base if exp % 2 else 1)
|
||||
```
|
||||
|
||||
## 两种方法对比
|
||||
|
||||
| 特性 | 二进制法 | 折半法 |
|
||||
| :--- | :--- | :--- |
|
||||
| **实现方式** | 迭代 + 位运算 | 递归/迭代 + 分治 |
|
||||
| **时间复杂度** | $O(\log N)$ | $O(\log N)$ |
|
||||
| **空间复杂度** | $O(1)$ | $O(\log N)$ (递归) |
|
||||
|
||||
两者都可以实现在指数时间解决问题,二进制方法比折半法更加的省空间。
|
||||
320
docs/blog/technology/python_string_format.md
Normal file
320
docs/blog/technology/python_string_format.md
Normal file
@@ -0,0 +1,320 @@
|
||||
---
|
||||
title: Python 字符串格式化全指南
|
||||
createTime: 2026/01/09 14:00:48
|
||||
cover: /images/elysia/7.jpg
|
||||
coverStyle:
|
||||
layout: left
|
||||
permalink: /archives/56ea3081-9c69-43d7-96c8-2812ec08be2c/
|
||||
---
|
||||
|
||||
## 字符串格式化
|
||||
|
||||
我们经常会输出类似“亲爱的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 字符串格式化的代码示例:**
|
||||
|
||||
```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("%r"%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)) #居中且使用*填充
|
||||
```
|
||||
|
||||
**运行结果:**
|
||||
|
||||
```text
|
||||
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. 使用位置参数**
|
||||
|
||||
```python
|
||||
# 按照位置一一对应
|
||||
print('{} asked {} to do something'.format("egon","lili"))
|
||||
print('{} asked {} to do something'.format("lili","egon"))
|
||||
```
|
||||
|
||||
**2. 使用索引**
|
||||
|
||||
```python
|
||||
# 使用索引取对应位置的值
|
||||
print('{0}{0}{1}{0}'.format('x','y'))
|
||||
```
|
||||
|
||||
**3. 使用关键字参数 or 字典**
|
||||
|
||||
```python
|
||||
# 可以通过关键字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. 填充和格式化**
|
||||
|
||||
```python
|
||||
# 先取到值,然后在冒号后设定填充格式:[填充字符][对齐方式][宽度]
|
||||
|
||||
# *<10:左对齐,总共10个字符,不够的用*号填充
|
||||
print('{0:*<10}'.format('开始执行')) # 开始执行******
|
||||
|
||||
# *>10:右对齐,总共10个字符,不够的用*号填充
|
||||
print('{0:*>10}'.format('开始执行')) # ******开始执行
|
||||
|
||||
# *^10:居中显示,总共10个字符,不够的用*号填充
|
||||
print('{0:*^10}'.format('开始执行')) # ***开始执行***
|
||||
```
|
||||
|
||||
**5. 进度和进制**
|
||||
|
||||
```python
|
||||
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. {} 中可以是变量名**
|
||||
|
||||
```python
|
||||
name = 'egon'
|
||||
age = 18
|
||||
print(f'{name} {age}') # egon 18
|
||||
print(F'{age} {name}') # 18 egon
|
||||
```
|
||||
|
||||
**2. {} 中可以是表达式**
|
||||
|
||||
```python
|
||||
# 可以在{}中放置任意合法的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. 在类中的使用**
|
||||
|
||||
```python
|
||||
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**
|
||||
|
||||
```python
|
||||
# 当格式化字符串过长时,如下列表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. 引号的嵌套**
|
||||
|
||||
```python
|
||||
# 当字符串嵌套发送冲突时,与正常的字符串处理方式是一样的
|
||||
# 1、外层为单引号,内层嵌套也为单引号,并且想要输入的内容也为单引号,那么外层需要改用双引号
|
||||
print("my name is 'egon'")
|
||||
|
||||
# 2、外层为单引号,内层嵌套也为单引号,并且想要输入的内容也为单引号,需要用到转义
|
||||
print('my name is \'egon\'')
|
||||
```
|
||||
|
||||
**6. 括号的处理**
|
||||
|
||||
需要注意的是,反斜杠可以用来进行字符转义,但不能用在 {} 的表达式中,注释 # 号也不能出现在 {} 的表达式中。
|
||||
|
||||
```python
|
||||
f'{1\2}' # 语法错误
|
||||
f'{x#}' # 语法错误
|
||||
```
|
||||
|
||||
所以当我们的输出的结果中需要包含{}时,类似于之前输出%的做法,需要在原有的基础上再套一层。
|
||||
|
||||
```python
|
||||
print(f'{{天王盖地虎}}') # {天王盖地虎}
|
||||
print(f'{{{{天王盖地虎}}}}') # {{天王盖地虎}}
|
||||
```
|
||||
|
||||
#### 四、 三种字符串格式化方式的性能对比
|
||||
|
||||
```python
|
||||
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语言的核心语法特征,使用方式如下。
|
||||
|
||||
```python
|
||||
from string import Template
|
||||
|
||||
name='EGON'
|
||||
t = Template('Hello $name!')
|
||||
res=t.substitute(name=name)
|
||||
|
||||
print(res) # Hello EGON!
|
||||
```
|
||||
|
||||
还有另外一个不同的地方是,这个模板字符串不支持类似str.format那样的进制转换,需要自己处理。
|
||||
|
||||
```python
|
||||
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的最佳的时机就是当你的程序需要处理由用户提供的输入内容时。模板字符串是最保险的选择,因为可以降低复杂性。其他一些复杂的字符串格式化技巧的可能会给你的程序带来安全漏洞。
|
||||
|
||||
### 总结
|
||||
|
||||
- 如果格式化的字符串是由用户输入的,那么基于安全性考虑,推荐使用Template。
|
||||
- 如果使用的python3.6+版本的解释器,推荐使用f-Strings。
|
||||
- 如果要兼容python2.x版本的python解释器,推荐使用str.format。
|
||||
- 如果不是测试的代码,不推荐使用%。
|
||||
Reference in New Issue
Block a user