🐍 第2章 变量与条件判断程序
📄 一句话概括

同一个加号,两边都是字符串就拼接,两边都是数字就求和,一边一样就报错。 input() 拿到的永远是字符串。该算数的地方忘了转,程序不会报错,它会把 21 变成 2121。 f-string 是拼输出的正路;但中文用它也对不齐,因为宽度是按字符数算的,一个汉字占两格。

📝 本章和前面几章的关系

上一篇学了 if 怎么改变走向,这一篇处理数据本身:字符串怎么切、类型怎么转、输出怎么排。 教材第 2 章后三节是【处理字符串数据】【字符串处理与数据类型转换】【格式化字符串】。 这三节是全书出现频率最高的内容。后面每一章都在拼字符串、转数字、打印结果。 本篇有个反直觉的结论要提前说: 官方文档说 f-string 能对齐,中文它也对不齐。演示里两种写法都摆了,你能看到两只手一起歪。

一、加号的两副面孔

'3' + '4' 得 '34',3 + 4 得 7。同一个符号,两种意思:拼接和求和。 决定用哪种的不是符号,是两边的类型。两边都是字符串就接起来,两边都是数字就加起来。 一边字符串一边数字呢?Python 不猜,直接报错:can only concatenate str (not "int") to str。

这条报错信息值得读一遍:它没说「类型不对」,它说「只能把字符串接到字符串上」。 把错误信息读明白,比背语法有用。

二、转换:三个函数管三种去向

int() 去整数,float() 去小数,str() 去文字。三个函数,解决九成的类型问题。 有一个坑必须知道:int('3.5') 报错。因为 int() 只认「纯整数的写法」,它不负责解析小数点。 要转得绕一下:int(float('3.5')),先变 3.5 再截成 3。 注意是截断不是四舍五入,int(3.9) 也是 3。

反过来,str(3.9) 得到 '3.9',不丢精度。 float('3') 得到 3.0,整数转小数永远无损,小数转整数永远有损。 还有一个反直觉的:int() 能吃掉首尾空格,int(' 42 ') 是 42。 但它吃不下千分位逗号,int('1,000') 直接报错,得先 replace(',', '')。

三、input 拿到的一定是字符串

演示第三段是全书最该记住的一个坑。 程序想算 21 + 21,但 input() 给的是字符串 '21',加号于是做了拼接,输出 2121。 它不报错,它给你一个看起来很合理的错答案。 这类错误比报错难查十倍。 规矩只有一条:input() 的返回值,在拿去算之前必须转。n = int(input('几件?')),一行解决。

如果用户可能输入非数字,int() 会抛 ValueError。 第 5 章讲 try except 时会处理这个,现在先知道它会炸就行。

四、格式化:三种写法,一种推荐

写法 代码 什么时候用
f-string f"{v:.2f}" 新代码一律用它
format "{:.2f}".format(v) 读老代码时要认得出
百分号 '%.2f' % v 只在看旧教程时遇到

三种算出来的结果完全一样。 f-string 好在变量名直接写在花括号里, 不用数后面有几个占位符——占位符和参数的顺序错位,是百分号写法最常见的 bug。 冒号后面那段叫格式说明,几个常用记号得记住:< 左对齐、> 右对齐、^ 居中、数字是宽度、 ,.2f 是千分位加两位小数。 例:f"{price:>8,.2f}" 意思是「右对齐、占 8 格、带千分位、两位小数」。

五、中文为什么对不齐

演示里同一张表用百分号和 f-string 各写一遍,结果列位置一模一样,也都歪。 原因不在写法,在「宽度」这条规矩: 格式说明里的宽度按字符个数算,一个汉字算一个字符;但屏幕上它占两格宽。 所以「螺丝」两个字按宽度算占 2,实际显示占 4,后面就少了两格,整列往左偏。

想真对齐只有两条路:按显示宽度自己补空格(一个汉字补两个), 或者干脆交给表格语法(本课的笔记就是用表格排的)。 日常打日志不必纠结。为了对齐把代码写复杂,是本末倒置。

六、字符串的几个常用方法

演示最后一段用的是同一条脏数据:' 2026-11-02, 库存告警, 螺丝只剩3个 '。

方法 做什么 结果
strip() 去掉首尾空白 '2026-11-02, 库存告警, 螺丝只剩3个'
split(',') 按分隔符切成列表 三段,后两段前面还带空格
replace(a, b) 换掉全部 a 把「螺丝」换成「膨胀螺栓」
startswith(s) 是不是以 s 开头 True
len(s) 有几个字符 28

split 有一个容易漏的细节:它不会顺手去空格。 用 , 切,切出来的第二段开头还带着一个空格。 两条路解决:切完每段 strip() 一次,或者干脆写 split(', ') 把空格含进分隔符。 后者更短,但如果有的地方逗号后没空格,就会切不开。 join 是 split 的反向操作:', '.join(['a', 'b']) 得 'a, b'。 注意它是「用谁去连」,所以 join 写在分隔符上,不写在列表上。

七、动手 Demo:字符串、数字、格式化:三种类型三条规矩

先摆清加号的两副面孔,再看三种转换函数各自认什么。 然后复现那个最坑的 2121。 最后把同一张表用百分号和 f-string 各排一遍,看中文到底歪在哪。

print('① 加号对字符串是拼接,对数字是求和')
print("  '3' + '4'   =", repr('3' + '4'))
print('  3 + 4      =', 3 + 4)
try:
    '3' + 4
except TypeError as e:
    print('  \'3\' + 4     = 报错', e)

print('② 要混着算,先转类型')
print('  int(\'3\') + 4     =', int('3') + 4)
print('  str(3) + \'4\'     =', repr(str(3) + '4'))
print('  float(\'3.5\') * 2 =', float('3.5') * 2)
try:
    int('3.5')
except ValueError as e:
    print('  int(\'3.5\')       = 报错', e)
print('  int(float(\'3.5\')) =', int(float('3.5')))

print('③ 输入进来的东西永远是字符串')
raw = input('请输入一个数:')
print('  输入 %s → %s %r %s' % (raw, type(raw).__name__, raw, raw + raw))
print('  转一下 →', int(raw) + int(raw))

print('④ 对齐输出:两种写法对比')
items = [('螺丝', 3, 4.5), ('扳手', 12, 288.0), ('电钻', 1, 399.0)]
print('  用百分号写:')
print('  %-8s %6s %12s' % ('品名', '数量', '金额'))
for n, q, p in items:
    print('  %-8s %6d %12.2f' % (n, q, p))
print()
print('  用 f-string 写同一张表:')
print('  %-8s %6s %12s' % ('品名', '数量', '金额'))
for n, q, p in items:
    print(f'  {n:<8}{q:>6}{p:>12,.2f}')

print('⑤ 三种格式化写法对照')
v = 3.14159
print('  f-string  ' + f'{v:.2f}')
print('  format    ' + '{:.2f}'.format(v))
print('  百分号    ' + '%.2f' % v)

print('⑥ 常用的字符串方法')
s = '  2026-11-02, 库存告警, 螺丝只剩3个  '
print('  原串   ', repr(s))
print('  strip  ', repr(s.strip()))
print('  split  ', s.strip().split(','))
print('  replace', repr(s.strip().replace('螺丝', '膨胀螺栓')))
print('  startswith', s.strip().startswith('2026'), '→', s.strip().startswith('2026'))
print('  len    ', len(s))

实跑输出:

--------------------------------------------------------------------
字符串、数字、格式化:三种类型三条规矩
--------------------------------------------------------------------

  ① 加号对字符串是拼接,对数字是求和
  --------------------------------------------------------------------
    '3' + '4'   = '34'
    3 + 4       = 7
    '3' + 4     = 报错 can only concatenate str (not "int") to str
  同是加号,两边都是字符串就拼接,两边都是数字就求和,一边一样就报错。Python 不会替你猜。

  ② 要混着算,先转类型
  --------------------------------------------------------------------
    int('3') + 4      = 7
    str(3) + '4'      = '34'
    float('3.5') * 2  = 7.0
    int('3.5')        = 报错 invalid literal for int() with base 10: '3.5'
  int() 只认纯整数写法。
  '3.5' 要先 float() 再 int(),或者 int(float('3.5')),结果会截掉小数变成 3。

  ③ 输入进来的东西永远是字符串
  --------------------------------------------------------------------
    输入 21 → str '21' 2121
  该求和的地方它把 21 重复了两遍。
  这是新手最常见的第一个坑:input() 拿到的是 str,算数前必须 int()。

  ④ f-string 是对齐输出的正路
  --------------------------------------------------------------------
    用百分号写:
    品名          数量          金额
    螺丝           3        4.50
    扳手          12      288.00
    电钻           1      399.00

    用 f-string 写同一张表:
    品名          数量          金额
    螺丝           3        4.50
    扳手          12      288.00
    电钻           1      399.00
  冒号后面是格式说明:< 左对齐、> 右对齐、8 是宽度、,.2f 是千分位加两位小数。
  百分号那套的 %-8s、%6d、%12s 是一回事,只是写法更老。
  两种写法算出来的列位置完全一样,也都歪。
  说明问题不在写法,而在「宽度按字符算」这条规矩——一个汉字算一格,屏幕上却占两格。
  想真对齐只有两条路:按显示宽度自己补空格,或者交给表格语法。
  日常打印日志不必纠结这个,别为了对齐把代码写复杂。

  ⑤ 三种格式化写法对照
  --------------------------------------------------------------------

| 写法 | 代码 | 结果 |
|---|---|---|
| f-string | f"{v:.2f}" | 3.14 |
| format | "{:.2f}".format(v) | 3.14 |
| 百分号 | '%.2f' % v | 3.14 |

  三种都能用,f-string 最直白:变量名直接写在花括号里,不用数后面有几个占位符。新代码优先用它。

  ⑥ 常用的字符串方法
  --------------------------------------------------------------------
    原串   '  2026-11-02, 库存告警, 螺丝只剩3个  '
    strip  '2026-11-02, 库存告警, 螺丝只剩3个'
    split  ['2026-11-02', ' 库存告警', ' 螺丝只剩3个']
    replace '2026-11-02, 库存告警, 膨胀螺栓只剩3个'
    startswith '2026' → True
    len    28
  strip 去掉首尾空白,split 按分隔符切成列表,join 反过来把列表拼成串。
  注意 split 不会顺手去空格,切出来的第二段前面还带着一个空格——要么切完再 strip,
  要么写 split(', ') 把分隔符本身含进去。

第一段三行连起来看:同样是加号,两边都是字符串就拼接,一边一样就报错。 Python 不会替你猜「你是想拼还是想加」,它只会拒绝。 第二段那个 int('3.5') 报错,是新手第二个高频坑。int() 只认纯整数写法,带小数点得先 float()。 注意 int(float('3.5')) 得 3,是截断不是四舍五入。 第三段是本篇的重点。输入 21,输出 2121——它把 21 重复了两遍。程序没报错,只是算错了。这种错最难查。 第四段两张表列位置完全一样,也都歪。 说明问题不在写法,而在宽度按字符数算:一个汉字算一格,屏幕上占两格。 第六段的 split 结果里,后两段前面还带着空格。 split 不会顺手去空格,要么切完再 strip,要么把分隔符写成逗号加空格。

30 秒实验

  1. 把演示第四段的 {n:<8} 改成 {n:<10},看列位置往哪挪,验证宽度是按字符数算的。
  2. 把演示第二段的 int(float('3.5')) 改成 round(float('3.5')), 再试 round(4.5) 和 round(3.5),看 Python 的「四舍六入五成双」是怎么表现的。
  3. 把演示第六段的 split(',') 改成 split(', '),看切出来的第二段前面还有没有空格。

常见误区

  1. 「加号就是加法。」对字符串它是拼接。 决定用哪种意思的是两边的类型,不是符号。一边一样 Python 直接报错,不会替你猜。
  2. 「int() 能把 '3.5' 转成 3。」它直接报错。int() 不解析小数点,得先 float()。 而且 int() 是截断,不是四舍五入。
  3. 「f-string 能对齐中文,比百分号强。」两种写法算出来一模一样,也都歪。 宽度按字符数算,一个汉字算一格,屏幕上占两格,这是格式说明管不到的地方。
自测三问(点开对答案) 1. `'3' + 4` 为什么报错,而 `int('3') + 4` 就没问题? —— 因为加号要求两边同类型:两边都是字符串就拼接,两边都是数字就求和。 `'3'` 是字符串、`4` 是整数,Python 不猜你想干什么,直接拒绝。 `int('3')` 先把字符串变成数字 3,两边就都是数字了。 2. 用户输入 21,程序想算两倍,结果打印出 2121。哪里错了? —— `input()` 返回的是字符串 `'21'`,`'21' + '21'` 做的是拼接,不是加法。 要写成 `int(input(...)) * 2`。**这类错误不报错,只给错答案,最难查。** 3. 用 f-string 写 `{name:<8}`,名字是「螺丝」两个字,为什么后面还是没对齐? —— 因为宽度按字符个数算。「螺丝」是两个字符,`:<8` 会补 6 个空格; 但屏幕上两个字占四格宽,视觉上一共占了 10 格,比按宽度算的 8 格多两格。 **字符数和显示宽度不是一回事。**

下一篇进入第 3 章:一次要处理很多笔数据时,列表和循环怎么配合。

相关笔记