第二章 Python 语言基础
第一章我们把环境搭起来了,写了个能跑的猜数字游戏。但有件事我没敢在那时候告诉你,第二章让我们来亲身感受下。
打开命令行试一下这行。
python
>>> 0.1 + 0.2
0.30000000000000004
别急着怀疑电脑,也别怀疑 Python,这个结果有它自己的道理,本章会把它讲清楚。
上一章我们写的是"能跑就行"的代码,这一章要学怎么写"对的"代码。变量怎么存东西、数据分哪几类、运算符有哪些、输入输出怎么做得体面。学完你能写出带计算、带格式化输出、能跟人交互的小工具。
我会按照下面内容讲解。
python
第二章 Python 语言基础
│
├── 1. 语法特点 → 缩进、换行、注释、大小写、动态类型
├── 2. 保留字与标识符 → 35 个不能碰的词 + 命名规矩
├── 3. 变量 → 它是标签不是盒子,以及三种赋值写法
├── 4. 基本数据类型 → 数字 / 字符串 / 布尔 / None + 互相转换
├── 5. 运算符 → 算术、比较、逻辑、优先级
├── 6. 基本输入输出 → print 的隐藏参数、input 的返回类型
├── 7. 实战 → 名片 / BMI / 小票 / 秒数换算
└── 8. 小结 → 一张表收口 + 5 道题 + 下一章预告
老规矩,别只阅读。把代码敲进你的常用IDE,跑一遍,改点参数再跑一遍。
一、Python 语法特点
1.1 用缩进来分块,不用花括号
这是 Python 最显眼的特征。别的语言用 {} 包住代码块,Python 用缩进。
c
// C 语言,靠花括号
if (score >= 60) {
printf("及格");
printf("可以回家了");
}
printf("结束");
python
# Python,靠缩进
if score >= 60:
print("及格")
print("可以回家了")
print("结束")
每条内容给你讲解下。
bash
if score >= 60: ← 行尾的冒号,等于说"下面要开新块了"
print("及格") ← 缩进 4 空格,属于 if 的地盘
print("可以回家了") ← 同样缩进,还是 if 的地盘
print("结束") ← 退回行首,已经出了 if,无论如何都会执行
四条规矩记牢。
- 同一个代码块里,每一行的缩进量必须一模一样。
- 推荐 4 个空格,别用 Tab。混用会报
IndentationError(第一章 Q6 讲过怎么解决)。 - 冒号
:是"下面要开新块"的信号灯,看到if/for/def后面跟着冒号,下一行就得缩进。 - 缩进结束,代码块就结束。没有
end,也没有}。
我刚开始写 Python 的时候,最不习惯的就是这个。写了两周之后反倒回不去了,看别的语言那一堆花括号会觉得多余。
1.2 一行一句,分号可有可无
Python 里换行就代表这句话说完了,不用加分号。
python
a = 1
b = 2
非要写在一行也行,用分号隔开。
python
a = 1; b = 2 # 语法没问题,但没人这么写
一句话太长想换行,用反斜杠或者直接套括号。
python
total = 1 + 2 + 3 + \
4 + 5 + 6
result = (1 + 2 + 3 +
4 + 5 + 6)
我个人偏好括号那一种,反斜杠后面不小心多打个空格就报错,太脆了。
1.3 注释
单行注释用 #。
python
# 这是单行注释
price = 19.9 # 行尾也可以写注释
多行说明用三个引号包起来。严格说它是个字符串字面量,但放在文件开头或者函数下面,Python 会把它当成文档字符串(docstring)。
python
"""
价格计算模块
汇率按 7.2 算,用之前记得确认下最新汇率
"""
注释这事我只有一条建议,写为什么,别写做什么。
python
# ❌ 废话注释,代码本身已经说了在干嘛
price = price * 0.8 # 价格乘以 0.8
# ✅ 有价值,说清了 0.8 是怎么来的
price = price * 0.8 # 会员八折,运营给的口径
1.4 大小写敏感
Name、name、NAME 在 Python 眼里是三个完全不同的东西。写错大小写是最常见的低级错误之一,报错信息通常是 NameError: name 'xxx' is not defined。
1.5 动态类型,万物皆对象
Python 的变量没有固定类型,同一个变量可以随时装不同的东西。
python
x = 10 # x 现在是整数
x = "hello" # 现在变成了字符串,完全合法
x = [1, 2, 3] # 又变成了列表
想知道一个东西是什么类型,问 type()。
python
>>> type(10)
<class 'int'>
>>> type(3.14)
<class 'float'>
>>> type("abc")
<class 'str'>
对比一下静态语言。Java 里写 int x = 10; 之后,x 这辈子只能是整数,塞个字符串进去编译器直接报错。Python 没有这层约束,写起来快,代价是类型错误要等到运行时才暴露。
二、保留字与标识符
2.1 35 个不能碰的保留字
保留字(关键字)是 Python 自己留着用的词,你不能拿它们当变量名。想知道有哪些,直接问解释器。
python
import keyword
print(keyword.kwlist)
print(len(keyword.kwlist)) # 35
按用途分个组,看着就不乱了。
| 分组 | 保留字 |
|---|---|
| 逻辑与空值 | True False None and or not is in |
| 控制流 | if elif else for while break continue pass |
| 定义与返回 | def class lambda return yield |
| 异常处理 | try except finally raise assert |
| 作用域与删除 | global nonlocal del |
| 导入与上下文 | import from as with |
| 异步协程 | async await |
几个容易踩的点。
TrueFalseNone首字母必须大写 。写成true或none会报NameError,这是从其他语言转过来的人最常犯的错。matchcasetype_属于软关键字,Python 3.10 引入match语句时加的。它们只在特定位置有特殊含义,别的地方还能当变量名用,但我不建议这么干,容易把读代码的人绕晕。- 别拿内置函数名当变量。
liststrdictidtypesummax这些不是保留字,你确实能把它们覆盖掉,然后整段代码就开始莫名其妙地报错。
python
# 我年轻时候干过的蠢事
list = [1, 2, 3]
print(list) # 还行,[1, 2, 3]
x = list("abc") # TypeError,list 已经被你改成列表了,不再是那个函数
2.2 标识符命名规则
标识符就是你给变量、函数、类起的名字。规则很简单。
| 名字 | 合法吗 | 原因 |
|---|---|---|
name |
✅ | 字母组成 |
user_name |
✅ | 字母加下划线 |
_count |
✅ | 下划线开头 |
age2 |
✅ | 数字不在开头就行 |
姓名 |
✅ | Python 3 支持 Unicode 标识符 |
2age |
❌ | 数字开头 |
user-name |
❌ | 中划线会被当成减号 |
user name |
❌ | 不能有空格 |
class |
❌ | 保留字 |
$price |
❌ | 特殊字符只允许下划线 |
中文变量名语法上合法,我见过有人这么用。两年之后他自己回来看代码也看不懂,别学。
2.3 命名约定
规则是强制的,约定是自发的。按 PEP 8(Python 官方代码风格指南)来就行。
| 场景 | 约定 | 例子 |
|---|---|---|
| 变量、函数 | 小写 + 下划线 | user_name get_price() |
| 常量 | 全大写 + 下划线 | MAX_RETRY PI |
| 类名 | 大驼峰 | StudentInfo |
| 内部使用 | 单下划线开头 | _helper() |
| 魔术方法 | 双下划线包起来 | __init__ __str__ |
名字长短这事我有点体会,宁可长一点也别缩写到看不懂 。usr_nm 这种省三个字符的写法,半年后你得靠猜。
2.4 三个自检小技巧
python
import keyword
keyword.iskeyword("class") # True,是保留字
keyword.iskeyword("name") # False,可以放心用
"user_name".isidentifier() # True,是合法标识符
"2age".isidentifier() # False
起名字拿不准的时候,这两个函数比翻文档快。
三、变量
3.1 变量是标签,不是盒子
很多入门书把变量画成一个装东西的盒子,这个比喻在 Python 里会带你走偏。更准确的形容是这样。
css
a = 10
┌────────┐ ┌───────────────┐
│ a │─────────▶│ 整数对象 10 │
└────────┘ └───────────────┘
变量名 数据躺在内存里
b = a
┌────────┐ ┌───────────────┐
│ a │─────────▶│ 整数对象 10 │
├────────┤ ▲ └───────────────┘
│ b │─────┘
└────────┘ 两个标签,贴的是同一个对象
变量就是一张贴在对象上的标签。赋值这个动作,是把标签贴到某个对象上。
理解这一点,下面这段代码的结果你就能预测了。
python
a = 10
b = a
a = 20 # a 撕下来贴到 20 上,b 还贴在 10 上
print(b) # 10,没跟着变
换成列表就不一样了。
python
a = [1, 2, 3]
b = a
a.append(4) # 改的是对象本身,不是换标签
print(b) # [1, 2, 3, 4],b 也跟着变了
区别在于整数是不可变对象,改不了只能换一个;列表是可变对象,能在原地改内容。这个话题第四章讲列表时还会展开。
3.2 三种赋值写法
python
# 1. 单个赋值
name = "张三"
# 2. 多元赋值,一次给多个变量,等号两边数量必须一致
x, y, z = 1, 2.5, "abc"
# 3. 交换两个变量,Python 里不用临时变量
a, b = 1, 2
a, b = b, a # 交换完成,a 是 2,b 是 1
# 4. 链式赋值
pi = rate = 0.0
交换变量那一行我第一次见到的时候愣了一下,别的语言都得请出第三个变量当搬运工。多元赋值在后面拆包的时候还会用到,很好使。
3.3 id() 和 is
id() 返回对象在内存里的身份证号,is 比的就是这个号,而 == 比的是值。
python
x = [1, 2, 3]
y = [1, 2, 3]
print(x == y) # True,值一样
print(x is y) # False,两个各自独立的对象
日常判断相等,用 == 就够了。只有一种情况必须用 is,判断是不是 None。
python
result = None
if result is None: # ✅ 正确写法
print("还没算出来")
if result == None: # ❌ 别这么写
print("还没算出来")
顺带提一句,CPython 把 -5 到 256 之间的整数做了缓存复用,所以这个范围内用 is 比较会得到一些反直觉的结果。别去依赖它,那是实现细节,不是语言规则。
3.4 del 与常量
del 删掉的是标签,不是对象。
python
a = 10
del a
print(a) # NameError,标签没了
Python 里没有真正的常量。PI = 3.14159 只是大家约定全大写代表别改,你硬要改它也不拦着。靠自觉。
四、基本数据类型
4.1 类型家族树
python
Python 内置类型
│
├── 数字 Number
│ ├── int 整数,精度无限
│ ├── float 浮点,双精度
│ ├── bool 布尔,int 的子类
│ └── complex 复数,用得少
│
├── 序列 Sequence
│ ├── str 字符串(不可变) ← 第五章细讲
│ ├── list 列表(可变) ← 第四章细讲
│ └── tuple 元组(不可变) ← 第四章细讲
│
├── 映射 dict 字典 ← 第四章细讲
├── 集合 set 集合 ← 第四章细讲
│
└── NoneType 只有一个值 None
本章只讲 int float bool str None 这五个,剩下的第四章之后逐个来。
4.2 数字
python
age = 18 # int
price = 19.9 # float
big = 2 ** 1000 # int,Python 的整数没有上限
2 ** 1000 这行在别的语言里会直接溢出,Python 算出来是一个 302 位的数,一点不慌。
python
>>> 2 ** 1000
10715086071862673209484250490600018105614048117055336074437503883703510511249361224931983788156958581275946729175531468251871452856923140435984577574698574803934567774824230985421074605062371141877954182153046474983581941267398767559165543946077062914571196477686542167660429831652624386837205668069376
浮点数是双精度,精度大约 15 到 16 位有效数字,末尾那点误差是二进制表示的锅。
布尔值在 Python 里是整数的子类,True 就是 1,False 就是 0。
python
>>> True + True
2
>>> isinstance(True, int)
True
它真正解释的是另一件事,为什么 if 1 和 if True 效果一样。
4.3 字符串
四种写法,各有各的用处。
python
s1 = '单引号'
s2 = "双引号"
s3 = """三引号,
可以跨行写"""
s4 = r"C:\new\test" # 原始字符串,反斜杠不当转义符
写 Windows 路径时 r"..." 能救命,不然 \n 会被当成换行。
转义字符就那么几个常用的。
| 转义 | 含义 |
|---|---|
\n |
换行 |
\t |
制表符 |
\\ |
一个反斜杠 |
\' \" |
引号本身 |
字符串可以按下标取字符,下标从 0 开始,负数表示从右往左数。
css
P y t h o n
0 1 2 3 4 5
-6 -5 -4 -3 -2 -1
python
s = "Python"
print(s[0]) # P
print(s[-1]) # n
print(s[1:4]) # yth,左闭右开,取到下标 4 之前
print(s[:3]) # Pyt
print(s[3:]) # hon
print(s[::-1]) # nohtyP,倒着来
切片是 s[起始:结束:步长],左闭右开。[::-1] 反转字符串这个技巧值得背下来。
常用方法挑几个高频的。
| 方法 | 作用 | 例子 |
|---|---|---|
len(s) |
长度 | len("abc") → 3 |
s.upper() |
转大写 | "abc".upper() → ABC |
s.lower() |
转小写 | 反过来 |
s.strip() |
去掉首尾空白 | 处理用户输入必备 |
s.split(",") |
按分隔符切开 | "a,b".split(",") → ['a', 'b'] |
"," .join(...) |
拼接 | 第五章用 |
s.replace(a, b) |
替换 | "a-b".replace("-", "") |
s.find("x") |
找位置,找不到返回 -1 | |
"x" in s |
判断子串,返回布尔 | "th" in "Python" → True |
字符串是不可变的,改单个字符会报错。
python
s = "Python"
s[0] = "p" # TypeError: 'str' object does not support item assignment
想要新字符串就重新赋值,s = "python"。
4.4 类型转换
python
int("18") # 18,字符串转整数
int(3.99) # 3,直接截断,不是四舍五入
float("3.14") # 3.14
str(100) # "100"
bool("") # False
bool("0") # True,非空字符串一律为真
几个坑。
| 表达式 | 结果 | 说明 |
|---|---|---|
int("3.14") |
报错 | 得先转 float 再转 int,int(float("3.14")) |
int("abc") |
报错 | 内容不是数字 |
int(3.99) |
3 | 向零截断,不是四舍五入 |
bool("0") |
True | 字符串 "0" 不是假值 |
str(None) |
"None" |
处理用户输入时 int() 最容易炸,因为用户什么都可能输。第一章那个温度转换器输入 abc 就崩,对吧。
4.5 格式化三种写法
第一种,% 占位符,老派写法,现在新代码里基本不写了,但你读老代码会遇到。
python
print("姓名:%s,年龄:%d" % ("张三", 18))
第二种,str.format(),Python 2.6 之后的主流。
python
print("姓名:{},年龄:{}".format("张三", 18))
第三种,f-string,Python 3.6 之后的首选,我强烈推荐这个。
python
name = "张三"
age = 18
print(f"姓名:{name},年龄:{age}")
f-string 里能直接写表达式,还能带格式说明符。
python
price = 3.14159
print(f"{price:.2f}") # 3.14,保留两位小数
print(f"{price:>10.2f}") # 右对齐占 10 位
print(f"{42:03d}") # 042,不足补零
print(f"{1234567:,}") # 1,234,567,千分位
print(f"{0.256:.1%}") # 25.6%,百分比
4.6 布尔与真值测试
下面这些东西在 if 里一律是假,其余都是真。
| 假值 | 说明 |
|---|---|
False None |
布尔假与空值 |
0 0.0 0j |
各种零 |
"" |
空字符串 |
[] () {} set() |
空容器 |
判断真假不用刻意转 bool,直接放进 if 就行。
python
name = input("请输入姓名:")
if name: # 空字符串就是假,不用写 name != ""
print(f"你好,{name}")
五、运算符
5.1 算术运算符
| 运算符 | 含义 | 例子 | 结果 |
|---|---|---|---|
+ |
加 | 7 + 2 |
9 |
- |
减 | 7 - 2 |
5 |
* |
乘 | 7 * 2 |
14 |
/ |
除 | 7 / 2 |
3.5 |
// |
整除 | 7 // 2 |
3 |
% |
取余 | 7 % 2 |
1 |
** |
幂 | 2 ** 10 |
1024 |
三个暗坑,逐个说。
第一,/ 永远返回浮点数,哪怕能整除。
python
>>> 10 / 5
2.0 # 注意那个 .0,类型是 float
>>> 10 / 3
3.3333333333333335
第二,// 是向下取整,不是四舍五入,负数方向要特别小心。
python
>>> 7 // 2
3
>>> -7 // 2
-4 # 不是 -3,它往更小的方向取整
第三,% 的结果符号跟着除数走,这一点跟 C、Java 不一样。
python
>>> 7 % 3
1
>>> -7 % 3
2 # 除数 3 是正数,结果就是正的
>>> 7 % -3
-2 # 除数是负数,结果跟着变负
5.2 比较运算符
== != > < >= <=,结果是布尔值。Python 有个别语言没有的糖,链式比较。
python
age = 25
if 18 <= age < 60: # 等价于 18 <= age and age < 60
print("在职年龄段")
5.3 逻辑运算符
and or not,三个都会短路求值,能省一步就省一步。
python
name = ""
result = name or "匿名用户" # name 为空,取后者
print(result) # 匿名用户
这里有个必须知道的细节,and 和 or 返回的不是布尔值,是操作数本身。
python
>>> 3 or 5
3 # 3 是真的,直接返回它,不看后面
>>> 0 or 5
5 # 0 是假的,返回后面那个
>>> 3 and 5
5 # 前面为真,结果取决于后面,返回 5
>>> 0 and 5
0 # 前面为假,直接返回它
记法是,or 找第一个为真的,and 找第一个为假的,都找不到就返回最后那个。
5.4 赋值运算符
python
count = 0
count += 1 # 等价于 count = count + 1
count *= 2
count -= 3
+= -= *= /= //= %= **= 全套都有。Python 里没有 ++ 和 -- ,写 i++ 不会报错(被当成两个正号),但也不会加一,这个坑我见过好几个人踩。
Python 3.8 加了个海象运算符 :=,能在表达式里顺手赋值。
python
# 不用海象
n = input("输入点什么:")
while n != "q":
print(n)
n = input("输入点什么:")
# 用海象
while (n := input("输入点什么:")) != "q":
print(n)
5.5 身份与成员运算符
python
a is b # 比身份证号(同一个对象)
a is not b
"th" in "Python" # 成员判断,字符串里就是子串判断
is 和 == 的区别前面讲过了,再强调一次,比值用 ==,比 None 用 is。
5.6 位运算符
了解即可。我写这么多年业务代码,用上位运算的场合一只手数得过来,基本就是权限位和状态标记这两类。
| 运算符 | 含义 |
|---|---|
& |
按位与 |
| ` | ` |
^ |
按位异或 |
~ |
按位取反 |
<< >> |
左移右移 |
python
>>> 6 & 3 # 110 & 011 = 010
2
>>> 1 << 3 # 左移三位,等于乘 8
8
5.7 优先级金字塔
ini
** 最高
~ +x -x (正负号)
* / // %
+ -
<< >>
&
^
|
== != < > <= >= (比较)
is is not in not in not and or
= += -= *= ... 最低
记不住就加括号。我本人就是这么干的,写了这么多年,遇到三四个运算符叠在一起的时候照样加括号,不丢人。括号还能让读代码的人一眼看懂你的意图。
5.8 浮点数精度问题
回到开头那个问题。
python
>>> 0.1 + 0.2
0.30000000000000004
这是二进制浮点的通病,换 Java、C 也一样,跟 Python 没关系。0.1 在二进制里是个无限循环小数,就像十进制里的 1/3 一样,存的时候只能截断,误差就这么攒下来了。
两个应对办法。
python
# 1. 只要显示好看,用 round 或格式化
print(round(0.1 + 0.2, 2)) # 0.3
print(f"{0.1 + 0.2:.2f}") # 0.30
# 2. 算钱这种要精确的场合,用 decimal
from decimal import Decimal
print(Decimal("0.1") + Decimal("0.2")) # 0.3
算钱一定用 Decimal,别用 float,这是行业共识。
六、基本输入和输出
6.1 print 的三个隐藏参数
大部分人只用到了 print() 的第一个参数,其实它还有三个能玩的。
python
print("苹果", "香蕉", "橙子") # 默认空格分隔
print("苹果", "香蕉", "橙子", sep=" | ") # 换成竖线分隔
print("加载中", end="") # 结尾不换行
print("完成")
第三个参数 end 默认是换行符 \n,改成空字符串就不换行了。用它刷个进度条。
python
import time
for i in range(101):
print(f"\r当前进度 {i}%", end="")
time.sleep(0.02)
print("\n搞定")
第四个参数 file 能把内容直接写进文件。
python
with open("log.txt", "w", encoding="utf-8") as f:
print("程序启动", file=f)
6.2 input 永远返回字符串
这一点第一章提过,这里再钉一次,因为它是新手 bug 的头号来源。
c
┌─── 终端 ────────────────────────────┐
│ 请输入年龄:18 │
│ │
│ 你拿到的是字符串 "18" │
│ int() 之后才是数字 18 │
│ "18" + "1" = "181" ← 拼接 │
│ int("18") + 1 = 19 ← 加法 │
└─────────────────────────────────────┘
python
age = input("请输入年龄:") # 用户输入 18
print(age + 1) # TypeError,字符串不能跟整数相加
print(int(age) + 1) # 19
6.3 格式化输出实战
想要输出看起来像那么回事,掌握这四个就够用了。
| 写法 | 效果 | 输出 |
|---|---|---|
f"{3.14159:.2f}" |
保留两位小数 | 3.14 |
f"{42:03d}" |
补零到三位 | 042 |
f"{1234567:,}" |
千分位 | 1,234,567 |
f"{0.256:.1%}" |
百分比 | 25.6% |
f"{'abc':>10}" |
右对齐占 10 位 | 前面补 7 个空格 |
f"{'abc':^10}" |
居中 | 两边各补空格 |
对齐在做表格和小票的时候特别有用,下面的实战三会用到。
七、实战
建个 D:\code\python-learn\chapter02\ 目录,本章代码都放这儿。
实战一|个人信息名片
练的是变量、多行字符串和 f-string。
python
# card.py
name = "张三"
job = "后端工程师"
city = "杭州"
years = 3
skills = "Python / MySQL / Docker"
card = f"""
╔══════════════════════════════════╗
║ 个人名片 ║
╠══════════════════════════════════╣
║ 姓名:{name} ║
║ 职业:{job} ║
║ 城市:{city} ║
║ 年限:{years} 年 ║
║ 技能:{skills} ║
╚══════════════════════════════════╝
"""
print(card)
print(f"{name} 在 {city} 做了 {years} 年 {job}")
运行结果。
╔══════════════════════════════════╗
║ 个人名片 ║
╠══════════════════════════════════╣
║ 姓名:张三 ║
║ 职业:后端工程师 ║
║ 城市:杭州 ║
║ 年限:3 年 ║
║ 技能:Python / MySQL / Docker ║
╚══════════════════════════════════╝
张三 在 杭州 做了 3 年 后端工程师
右边框对不齐,对吧。中文字符在终端里占两个英文字符的宽度,靠数空格对齐是治不好的,得按显示宽度算。这也是为什么做小票那种对齐输出时,我更愿意用下面的等宽思路。
实战二|BMI 计算器
练的是输入、类型转换、算术运算符和分支判断。
python
# bmi.py
name = input("请输入姓名:")
weight = float(input("请输入体重(kg):"))
height = float(input("请输入身高(m):"))
bmi = weight / (height ** 2)
print(f"\n{name} 的 BMI 指数是 {bmi:.1f}")
if bmi < 18.5:
level = "偏瘦"
elif bmi < 24:
level = "正常"
elif bmi < 28:
level = "超重"
else:
level = "肥胖"
print(f"身体状态:{level}")
跑一遍。
请输入姓名:张三
请输入体重(kg):70
请输入身高(m):1.75
张三 的 BMI 指数是 22.9
身体状态:正常
这里有两个细节。height ** 2 比 height * height 更直观,指数运算就用 **。输出用 :.1f 保留一位小数,不然 BMI 会甩出一长串小数。
实战三|购物小票
练的是运算符、f-string 对齐和格式化。这个例子我很喜欢,因为它输出的成果看着就像个正经东西。
python
# receipt.py
shop_name = "街角便利店"
item1, price1, count1 = "牛奶", 3.5, 2
item2, price2, count2 = "面包", 8.0, 1
item3, price3, count3 = "咖啡", 12.8, 3
amount = price1 * count1 + price2 * count2 + price3 * count3
discount = 0.9 # 会员九折
payable = amount * discount
payable = round(payable, 2) # 算钱保留两位
line = "-" * 42
print(f" {shop_name}")
print(line)
print(f"{'商品':<8}{'单价':>8}{'数量':>8}{'小计':>12}")
print(line)
print(f"{item1:<8}{price1:>8.2f}{count1:>8}{price1 * count1:>12.2f}")
print(f"{item2:<8}{price2:>8.2f}{count2:>8}{price2 * count2:>12.2f}")
print(f"{item3:<8}{price3:>8.2f}{count3:>8}{price3 * count3:>12.2f}")
print(line)
print(f"{'合计':<8}{'':>8}{'':>8}{amount:>12.2f}")
print(f"{'折扣':<8}{'':>8}{'':>8}{discount:>11.1%}")
print(f"{'应付':<8}{'':>8}{'':>8}{payable:>12.2f}")
print(line)
print(" 谢谢惠顾,欢迎下次光临")
输出长这样。
markdown
街角便利店
------------------------------------------
商品 单价 数量 小计
------------------------------------------
牛奶 3.50 2 7.00
面包 8.00 1 8.00
咖啡 12.80 3 38.40
------------------------------------------
合计 53.40
折扣 90.0%
应付 48.06
------------------------------------------
谢谢惠顾,欢迎下次光临
几个值得学的点。
f"{'牛奶':<8}"里的<8是左对齐占 8 个字符宽,>8是右对齐。数字右对齐才符合看小票的习惯。price1, count1 = 3.5, 2这种多元赋值,把三个商品的参数一次性摆开,比一行行写清爽。- 算钱那一步
round(payable, 2)不能省,不然48.060000000000002这种结果会直接打印出来。 '折扣'那行用了:>11.1%,百分比格式自带一个%号,所以宽度比别的多留一位。
等等,{discount:>11.1%} 里的 discount 是 0.9,输出是 90.0%。如果你想要显示"打九折",写成 f"{(1 - discount) * 100:.0f}% 优惠" 更直白。
加练|秒数换算成时分秒
// 和 % 的经典练习题,面试也爱考。
python
# seconds.py
total = int(input("请输入总秒数:"))
hours = total // 3600
minutes = total % 3600 // 60
seconds = total % 60
print(f"{total} 秒 = {hours} 小时 {minutes} 分 {seconds} 秒")
跑一下。
yaml
请输入总秒数:3725
3725 秒 = 1 小时 2 分 5 秒
拆解一下这三行。// 3600 拿到能被整除的小时数,% 3600 拿到扣掉小时后剩下的秒数再 // 60 就是分钟,最后 % 60 剩下的是秒。想让输出更好看点,可以补零,f"{hours:02d}:{minutes:02d}:{seconds:02d}"。
八、小结
8.1 本章知识小结
| 模块 | 你现在应该能做对的事 | 自测 |
|---|---|---|
| 语法特点 | 说清缩进规则和注释写法 | □ 能解释为什么 Python 不用花括号 |
| 保留字 | 知道 35 个保留字,会查 | □ 能用 keyword.iskeyword() 判断 |
| 标识符 | 起合规且规范的名字 | □ 能说出 snake_case 用在哪 |
| 变量 | 理解标签语义,会三种赋值 | □ 能解释 a, b = b, a 为什么能交换 |
| 数据类型 | 分得清 int / float / str / bool / None | □ 知道 bool("0") 是 True |
| 类型转换 | 会 int() float() str() 互转 | □ 知道 int("3.14") 会报错 |
| 运算符 | 会用算术比较逻辑,懂优先级 | □ 能说出 -7 // 2 等于几 |
| 输入输出 | 会用 print 参数和 f-string 格式化 | □ 能输出对齐的小票 |
8.2 五句话记住本章
- 缩进就是语法,冒号开块,4 空格,别混 Tab。
- 变量是标签 ,赋值是贴标签,
is比地址,==比值。 /永远得浮点 ,//向下取整,%符号跟除数。- input 拿到的一定是字符串,要计算就得转类型。
- 算钱用 Decimal,别用 float。
8.3 课后练习
- 温度转换器升级,输入摄氏度,同时输出华氏度和开尔文(K = ℃ + 273.15),都保留一位小数。
- 计算圆的周长和面积,输入半径
r,π 取 3.14159,两个结果都保留两位小数。 - 输入一个三位数,用
//和%把它拆成百位、十位、个位三个数字分别打印。 - 写个"个人信息录入",输入姓名、年龄、身高,用 f-string 拼成一句话输出,年龄那栏右对齐占 5 位。
- 挑战题,输入一个总天数,换算成「x 年 x 个月 x 天」(一年按 365 天算,一个月按 30 天算),想想余数的处理顺序。
8.4 下一章
第三章讲流程控制 ,if 分支的各种写法、for 和 while 循环、break 和 continue,还有 range() 这个循环好搭档。这一章写的 BMI 和小票,到时候都能用循环重写一遍,代码能短一半。
附录|运算符与内置函数速查卡
| 类别 | 运算符 / 函数 |
|---|---|
| 算术 | + - * / // % ** |
| 比较 | == != > < >= <= |
| 逻辑 | and or not |
| 赋值 | = += -= *= /= //= %= **= := |
| 身份 | is is not |
| 成员 | in not in |
| 类型转换 | int() float() str() bool() |
| 常用内置 | type() id() len() print() input() round() abs() max() min() |
记不住就回来查,这张表我当年贴在显示器边上贴了半年。