上一篇:03 变量与数据类型:给数据贴标签(数字 · 字符串 · 布尔 · type())
上篇练习参考答案
练习 1:三行自我介绍:定义 name、age、hourly_pay 三个变量(类型分别是 str、int、int),用一个 print 打印成一句话; 
练习 2(预测再验证):先不运行,写下你认为 type(“3.14”)、type(3.14)、type(3 == 3) 的输出;然后进交互模式验证,错了的写在评论区; type("3.14") → <class 'str'>(有引号是字符串);type(3.14) → <class 'float'>;type(3 == 3) → <class 'bool'>。前两个最容易错。 
练习 3(total 的类型):小票改造:把加班费小算盘改成你自己的真实时薪和加班时长,运行后把小票贴在评论区;顺手回答:你的 total 是 int 还是 float?为什么? total 是 float,因为 weekday_rate 与 weekend_rate 都是浮点数,整数乘浮点数结果就是浮点数。 
练习 4(“1.5” 疫苗)——答案比预想的更精彩:把 weekday_rate 改成 1.5 改成 “1.5”(加引号),运行看报什么错,按三段式写出错误类型、位置、修法——这道题就是为模块二"从 Excel 读出来的数字不能直接算"提前打的疫苗
把 weekday_rate = "1.5" 加上引号后,50 * 3 * "1.5" 并不报错,而是静悄悄生成一个把 "1.5" 重复 150 遍的巨长字符串(字符串乘整数 = 重复,第 2 篇的 "=" * 30 就是这个原理);直到算 total = weekday_pay + weekend_pay(字符串 + 浮点数)才爆炸: 
TypeError: can only concatenate str (not "float") to str
教训有两条:① 类型错误不一定在出错的那一行立刻报,可能先潜伏、后爆炸,甚至一路不报只是结果全错——这种"静默出错"比报错可怕得多;② type() 随时体检,是唯一可靠的防线。这也正是模块二"从 Excel 读出的数字先 type 检查再运算"的原因。
一、场景引入:真实世界的数据,从来不干净

第 3 篇我们亲手定义了干干净净的变量。但真实工作里,数据长这样:
- 从微信群里复制来的名单:" 张三 ,李四 ;王五 ";
- 从表单导出的邮箱:" xingyu@MAIL.com ";
- 财务系统吐出来的金额:"12580.456"(精确到厘,但你只想看到两位小数)。
办公自动化的一半工作量,就是把脏数据洗成规范数据。 而洗数据的工具箱,几乎全是字符串方法——这就是本篇标题里"第一刚需"的意思。上篇埋的 f-string 伏笔,本篇也正式揭晓。
二、本篇目标
三、知识点讲解
3.1 拼接与 f-string:文字组装流水线
方式一:+ 拼接——两边必须都是字符串(03 篇报错 1 的坑还历历在目):
first = "星"
last = "宇Py"
print(first + last) # 星宇Py

方式二:f-string(正式揭晓)——引号前加 f,{} 里的内容会被"求值后填进去"(注:python3 才支持f-string,python2 不支持f-string):
以下为 python2 运行 f-string 会报语法错误+中文乱码,所以我们课程都是在最新的 python3 里运行 如果有用 python2 的同学需要先按本专栏 01 环境搭建课 安装一个 python3 环境后再进行练习 
name = "星宇Py"
pay = 625.0
print(f"{name} 本月加班费 {pay} 元") # 星宇Py 本月加班费 625.0 元
print(f"明年目标:{pay * 2} 元") # {} 里可以放算式(pay乘2):1250.0 元
python3 环境运行 
f-string 的三个杀手锏,+ 拼接一个都做不到:
杀手锏一:数字直接放进来,不用 str() 变身——从此告别 TypeError 拼接报错,通过 f"{变量名}" 语法放入的变量无论之前是什么类型都会自动转为字符串。
杀手锏二:金额格式化。冒号后面接格式说明符,如::,.2f = 千分位 + 固定两位小数:
amount = 12580.456
print(f"{amount}") # 12580.456
print(f"{amount:.2f}") # 12580.46(两位小数)
print(f"{amount:,.2f}") # 12,580.46(千分位 + 两位小数)

给老板看的金额,一律 :,.2f。一行代码,专业感拉满。
杀手锏三:对齐排版。:<10 左对齐占 10 格、:^10 居中、:>10 右对齐(数字常用右对齐,个位对齐):
print(f"|{'项目':<6}|{'金额':>8}|")

以后打印小票、对账单,全靠它。本专栏后续所有代码的输出排版,默认用 f-string。
一句话结论:从今天起,python3里"往文字里塞值"只写 f-string。 + 拼接只用于两个字符串字面量直接相连的简单场景。
3.2 索引与切片:像切香肠一样取文字
字符串里的每个字符都有编号,从左往右 0 开始数 ,从右往左 -1 开始数:
word = "Python"
# 文字: P y t h o n
# 从左往右数: 0 1 2 3 4 5
# 从右往左数: -6 -5 -4 -3 -2 -1
print(word[0]) # P(第 0 个)
print(word[5]) # n(第 6 个字符,编号是 5)
print(word[–1]) # n(负数从右往左数,-1 是最后一个)
print(word[–2]) # o(负数从右往左数,-2 是倒数第二个)
print(word[–6]) # P(负数从右往左数,-6 是倒数第六个)

切片 [起:止]:含头不含尾(本篇最重要的四个字):
phone = "12345678910"
# 内容: 1 2 3 4 5 6 7 8 9 1 0
# 从左往右数: 0 1 2 3 4 5 6 7 8 9 10
# 从右往左数: -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1
print(phone[0:3]) # 123(编号 0、1、2,不含编号 3)
print(phone[3:7]) # 4567(编号 3、4、5、6,不含编号 7)
print(phone[–4:]) # 8910(省略"止"= 从倒数第4个一直取到末尾)
print(phone[:3]) # 123(省略"起"= 从头开始一直到编号3,不包括编号3本身)

三个组合拳立刻打一个实战——手机号脱敏(把中间四位打码,财务和运营发通知时的常规操作):
phone = "12345678910"
masked = phone[:3] + "****" + phone[–4:]
print(masked) # 123****8910

一行切片 + 一行拼接,过去手工打码的活儿变成了一行代码。len() 数一下字符串有几个字符(中文一个字算一个字符):len("你好Py") 是 4。
3.3 清洗三件套与常备工具
strip()——只剪两头的空格:
注:下面 + ‘|’ 是因为输出的结果里右侧空格是否存在肉眼看不出来,所以加一个标志让肉眼可以看到右侧是否有空格 
raw = " xing yuPY "
print(raw.strip() + '|') # xing yuPY|(两头空格全去,注意:中间的空格不管!strip 只管两头)
print(raw.lstrip() + '|') # xing yuPY |(lstrip 只去左边空格)
print(raw.rstrip() + '|') # xing yuPY|(rstrip 只去右边空格)
print(raw.lstrip().rstrip() + '|') # xing yuPY|(先 lstrip 只去左边空格,再 rstrip 只去右边空格 = strip 两头空格全去 )

split()——按分隔符切成几段。结果用下标取出(下标语法和 3.2 节同款,这套 [0] [1] 以后对列表也通用):
record = "星宇Py ; 12345678910 ;12580.456"
parts = record.split(";")
print(parts[0].strip()) # 星宇Py (注意分隔符右侧的空格会保留)
print(parts[1].strip()) # 12345678910(所以切完通常再 strip 一次)
print(parts[2].strip()) # 12580.456(.strip() 有空格去空格,如果没有空格也不会报错)

join()——把几段粘回一个字符串,和 split 是一对反操作:
names = ["张三", "李四", "王五"]
print("、".join(names)) # 张三、李四、王五

常备四件:replace(旧, 新) 替换、upper()/lower() 大小写转换、title() 每个单词首字母大写、in 判断包含:
print("123-456-78910".replace("-", "")) # 12345678910(洗掉横线)
print("xing yu".upper()) # XING YU(统一大写)
print("xingyu@MAIL.com".lower()) # xingyu@mail.com(统一小写,比对邮箱必备)
print("xing yuPY".title()) # Xing Yupy(姓名规范显示)
print("Py" in "Python") # True("在不在里面")
print("星" in "星宇Py") # True

工具箱清点完毕:插值找 f-string,截取找切片,清洗找 strip/split/join,加工找 replace/大小写,判断找 in。
四、完整实战:客户登记信息清洗器
场景:报名表单吐出来一条脏数据——前后空格、姓名大小写混乱、金额精度超标。我们要把它洗成规范记录。新建 客户信息清洗器.py(点击py名下载):
# ===== 客户登记信息清洗器 =====
# 模拟从表单收到的一条原始脏数据:前后空格、分隔符后空格不齐、大小写乱、金额精度超标
raw = " xing yuPY ; 12345678910 ; 12580.456 "
print("=" * 44)
print(" 客户登记信息清洗器")
print("=" * 44)
print(f"原始数据:{raw}")
print("-" * 44)
# 第一步:按分号切成三段(切完每段单独 strip)
parts = raw.split(";")
name_raw = parts[0]
phone_raw = parts[1]
amount_raw = parts[2]
# 第二步:逐项清洗
name = name_raw.strip().title() # 去两头空格 + 单词首字母大写
phone = phone_raw.strip() # 去两头空格
amount = float(amount_raw.strip()) # 字符串变身浮点数,才能参与算术
# 第三步:加工输出(切片脱敏 + 金额格式化)
phone_masked = phone[:3] + "****" + phone[–4:]
print(f"姓名:{name}")
print(f"手机:{phone_masked}(完整 {len(phone)} 位)")
print(f"金额:{amount:,.2f} 元(原始精度 {amount})")
# 第四步:join 粘回一条规范记录
record = "、".join([name, phone_masked, f"{amount:,.2f}元"])
print("-" * 44)
print(f"规范记录:{record}")
print("=" * 44)
# 附:大小写无关的包含判断(以后做筛选常用)
print(f"姓名里含 'xing' 吗:{'xing' in name.lower()}")
流程对照工具箱:split 切开 → strip 洗净 → title/切片/f-string 加工 → join 封装。这就是一条完整的数据清洗流水线,模块二处理 Excel 时把"从表单读"换成"从表格读",流水线原封不动。
五、运行效果

——————————————–
姓名:Xing Yupy
手机:123****8910(完整 11 位)
金额:12,580.46 元(原始精度 12580.456)
——————————————–
规范记录:Xing Yupy、123****8910、12,580.46元
============================================
姓名里含 'xing' 吗:True
对照看两个细节:金额从 12580.456 变成 12,580.46;手机号中间四位永远打码——原始数据不动,展示时脱敏,这是处理个人信息的安全习惯。
六、常见报错与排查
报错 1:IndexError: string index out of range(下标越界)
phone = "12345678910"
print(phone[11])

原因:11 位手机号的编号是 0~10,取 phone[11] 越界了。注意长度 len(phone) 是 11,但最大编号是 len(phone) – 1。 解决:拿不准先 print(len(x)) 数一遍;或改用切片——切片越界不报错,phone[5:99] 会安静地取到末尾为止。
报错 2:AttributeError: 'str' object has no attribute 'stripp'(方法名拼错)
" abc ".stripp()

原因:方法名拼写错误(stripp、replase、spilt 都是高频手滑)。 解决:看报错里的 Did you mean 提示;字符串方法都是对原字符串调用、返回新结果,所以必须写成 x = x.strip()——只写 x.strip() 不赋值,等于洗完澡又跳回泥坑。
报错 3:ValueError: could not convert string to float(变身失败)
float(" 12580.456元 ")

原因:float() 只认"纯数字字符串",末尾多个"元"字就变不了身。 解决:先洗再变——float("12580.456元".replace("元", "").strip())。顺序很重要:清洗在前,转换在后。
坑 1(不报错但结果错):f-string 忘写 f
print("{name} 你好") 不报错,但会原样打印 {name} 你好。看到输出里带花括号,第一反应就是检查引号前的 f。 
坑 2(不报错但结果错):strip 去不掉中间空格
"星 宇 Py".strip() 只去两头。想去掉所有空格用 "星 宇 Py".replace(" ", "")。 
七、练习作业(5 分钟)
下一篇预告:《05 列表:批量数据的容器》——一个变量装下一整份名单。增删改查、append/insert、遍历入门,你的代码将从"处理一条数据"升级为"处理一沓数据"。



