第二章 Python语言基础

第二章 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,无论如何都会执行

四条规矩记牢。

  1. 同一个代码块里,每一行的缩进量必须一模一样。
  2. 推荐 4 个空格,别用 Tab。混用会报 IndentationError(第一章 Q6 讲过怎么解决)。
  3. 冒号 : 是"下面要开新块"的信号灯,看到 if / for / def 后面跟着冒号,下一行就得缩进。
  4. 缩进结束,代码块就结束。没有 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

几个容易踩的点。

  • True False None 首字母必须大写 。写成 true 或 none 会报 NameError,这是从其他语言转过来的人最常犯的错。
  • match case type _ 属于软关键字,Python 3.10 引入 match 语句时加的。它们只在特定位置有特殊含义,别的地方还能当变量名用,但我不建议这么干,容易把读代码的人绕晕。
  • 别拿内置函数名当变量。list str dict id type sum max 这些不是保留字,你确实能把它们覆盖掉,然后整段代码就开始莫名其妙地报错。
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 五句话记住本章

  1. 缩进就是语法,冒号开块,4 空格,别混 Tab。
  2. 变量是标签 ,赋值是贴标签,is 比地址,== 比值。
  3. / 永远得浮点 ,// 向下取整,% 符号跟除数。
  4. input 拿到的一定是字符串,要计算就得转类型。
  5. 算钱用 Decimal,别用 float。

8.3 课后练习

  1. 温度转换器升级,输入摄氏度,同时输出华氏度和开尔文(K = ℃ + 273.15),都保留一位小数。
  2. 计算圆的周长和面积,输入半径 r,π 取 3.14159,两个结果都保留两位小数。
  3. 输入一个三位数,用 // 和 % 把它拆成百位、十位、个位三个数字分别打印。
  4. 写个"个人信息录入",输入姓名、年龄、身高,用 f-string 拼成一句话输出,年龄那栏右对齐占 5 位。
  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()

记不住就回来查,这张表我当年贴在显示器边上贴了半年。

相关推荐
129Lab1 小时前
BMS 核心算法:SOC 估算从安时积分到 LSTM-UKF 串级融合的 30 年演进(附 Python 实现)
python·算法·lstm·python3.11·bms·soc估算
129Lab1 小时前
电池热管理仿真的AI加速:用Python+PINN物理信息神经网络替代传统CFD的可行性探索
pytorch·python·cfd·pinn·物理信息神经网络·仿真加速·电池热管理
用户298698530141 小时前
Python 中 HTML 转 PDF 的实现方法
python·html·api
答案是你1 小时前
YOLOE 开放词汇检测 + ONNX / TensorRT 推理,开源了!
python·深度学习·yolo·目标检测·计算机视觉·视觉检测
2601_962885721 小时前
AlphaFeed 支持哪些 K 线周期?period参数怎么传?
前端·数据库·python
老歌老听老掉牙1 小时前
基于 Python 的顺次文本拼接:以 MCD 数控文件为例
python·数控·mcd
朝朝辞暮i1 小时前
VLA 系统学习第 7 课:loss.backward() 到底做了什么?——从计算图到反向传播
人工智能·python·深度学习·神经网络·vla
外收内放2 小时前
Python基础语法练习题(53-54)
python·学习
聪明蛋子哟2 小时前
Python与Java双栈实战:手撸一个支持RAG与Tool Calling的高性能Agent框架
java·开发语言·python