入门与基础类型
# 第 1 章 Python 入门与基础类型
# 目录介绍
# 1.1 环境搭建
📖 本章定位:从零开始——安装 Python、认识变量类型、掌握运算符和输入输出。学完后你将能写 50 行的简单脚本。
📌 下章预告:掌握变量之后,第 2 章会深入字符串、列表、字典、集合——Python 的数据容器全家桶。
# 1.1.1 Python 语言特点
Python 是由 Guido van Rossum 于 1991 年发布的高级编程语言。它的设计哲学是**"优雅"、"明确"、"简单"**——用最少、最可读的代码表达最清晰的逻辑。
跟 C/C++/Java 相比,Python 有四个杀手级特征:
- 解释型语言:不需要编译,写完就能运行——改一行代码立刻看到效果,调试效率极高
- 动态类型:变量不用声明类型,
x = 1和x = "hello"可以随时切换——极大缩短从想法到代码的距离 - 强制缩进:用缩进代替
{}表示代码块——强迫所有 Python 代码风格统一,读别人的代码像读自己的 - "电池内置"(Batteries Included):标准库自带 200+ 模块——文件操作、正则、网络、JSON、多线程……开箱即用
# 1.1.2 Python 应用领域
| 领域 | 典型场景 | 核心库/框架 |
|---|---|---|
| 数据科学与 AI | 数据分析、机器学习、深度学习 | NumPy、Pandas、PyTorch、TensorFlow |
| Web 后端 | REST API、全栈 Web、微服务 | Django、Flask、FastAPI |
| 自动化运维 | 脚本、系统管理、CI/CD | os、subprocess、Ansible |
| 爬虫与信息采集 | 网页抓取、数据采集 | Requests、Scrapy、BeautifulSoup |
| 测试 | 单元测试、接口测试、自动化测试 | unittest、pytest、Selenium |
| 桌面 GUI | 桌面应用程序 | PyQt、Tkinter、wxPython |
💡 一句话定位:选择 Python,就是选择让代码量降 3-5 倍、让开发效率升 3-5 倍——代价是运行速度慢一些。但在 AI/数据分析/爬虫/运维这些领域,开发效率远比运行速度重要。
# 1.1.3 安装 Python
第一步:检查是否已安装
python3 --version
# 或
python --version
2
3
如果显示 Python 3.x.y,说明已安装,直接跳过本节。
第二步:下载安装
- macOS:
brew install python@3.12或官网下载.pkg - Windows:官网
python.org下载安装包,务必勾选 "Add Python to PATH" - Linux:
sudo apt install python3(Debian)或sudo yum install python3(CentOS)
第三步:验证
python3 --version # Python 3.12.3(你的版本号可能不同)
pip3 --version # pip 24.0
2
# 1.1.4 第一个程序:Hello World
创建一个文件 hello.py:
print("Hello, Python!")
运行:
python3 hello.py
输出:
Hello, Python!
🔑 跟 C/C++ 对比:
// C++ 版
#include <iostream>
int main() {
std::cout << "Hello, C++!" << std::endl;
return 0;
}
2
3
4
5
6
差异一目了然:
- C++ 需要
#include引入头文件、main()入口函数、return 0退出码——13 行最小样本 - Python 一行搞定——没有 boilerplate(样板代码),所思即所得
# 1.1.5 交互式解释器
Python 最有特色的工具——交互式解释器(REPL:Read-Eval-Print Loop):
python3
>>> print("Hello")
Hello
>>> 1 + 2
3
>>> "hello" * 3
'hellohellohello'
>>> exit()
2
3
4
5
6
7
🔑 REPL 的教学价值:
- 不需要新建文件——打开终端就写
- 不需要编译——回车即执行、立即看到结果
- 不需要调试器——打印什么就是什么
- 一条语句一个知识点——学语法时这个效率是无敌的
💡 建议:学本节内容时,全程开着 Python REPL。每个代码示例都亲手敲一遍——速度比只读至少要翻倍。
# 1.1.6 综合案例与思考
综合案例:第一个 Python 脚本——个人信息卡片
# personal_info.py
# 第一个完整脚本:展示个人信息
print("=" * 40)
print(" 个人信息卡片")
print("=" * 40)
# 个人信息
name = "杨充"
age = 28
city = "深圳"
is_student = False
languages = ["Python", "C++", "Java"]
# 输出
print(f"姓名:{name}")
print(f"年龄:{age}")
print(f"城市:{city}")
print(f"是否学生:{is_student}")
print(f"掌握的编程语言:{', '.join(languages)}")
# 计算一些有趣的数据
print() # 空行
print("--- 有趣的数据 ---")
print(f"名字长度:{len(name)}")
print(f"名字反转:{name[::-1]}") # 字符串切片:倒序
print(f"掌握语言数量:{len(languages)}")
print("=" * 40)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
运行:
python3 personal_info.py
========================================
个人信息卡片
========================================
姓名:杨充
年龄:28
城市:深圳
是否学生:False
掌握的编程语言:Python, C++, Java
--- 有趣的数据 ---
名字长度:2
名字反转:充杨
掌握语言数量:3
========================================
2
3
4
5
6
7
8
9
10
11
12
13
14
案例知识融合:这个案例虽然简单,但已经包含了变量赋值、字符串、布尔值、列表(languages)、f-string 格式化、字符串切片([::-1])、len() 内置函数、join() 方法——10 行核心代码覆盖了 Python 入门最核心的 6 个知识点。
思考题:
- Python 的
print()函数底层是怎么把信息输出到终端的?它经历了哪些系统调用?(提示:sys.stdout、write()系统调用) - 如果
languages中有 100 个元素,', '.join(languages)和for 循环 + print哪种方式效率更高?为什么? - Python 变量不需要声明类型——这是优点还是缺点?在大型项目中动态类型会带来什么问题?(提示:类型注解
x: int = 1是如何改善这个问题的?)
# 1.2 变量与数据类型
# 1.2.1 变量命名规则
Python 的变量是名称绑定(name binding)——变量名是贴在对象上的"标签",不是"存储数据的槽位"。
# 规则一:字母/下划线开头,后面跟字母/数字/下划线
name = "张三" # ✅
_age = 18 # ✅ 下划线开头表示"内部变量"(约定)
user2 = "李四" # ✅ 可以有数字
2user = "错误" # ❌ SyntaxError:不能以数字开头
my-name = "错误" # ❌ SyntaxError:不能用连字符
# 规则二:大小写敏感
Name = "大写"
name = "小写"
print(Name, name) # 大写 小写——两个不同变量
# 规则三:不能使用保留字
# class = 1 # ❌ class 是保留字
# True = 2 # ❌ True 是内置常量
# if = 3 # ❌ if 是关键字
# 查看所有保留字
import keyword
print(keyword.kwlist)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
命名规范表:
| 约定 | 示例 | 适用场景 |
|---|---|---|
snake_case | user_name, max_count | 变量、函数(PEP 8 推荐) |
CamelCase | UserName, MaxCount | 类名 |
UPPER_CASE | MAX_SIZE, API_KEY | 常量(约定不变的值) |
_leading | _private_var | 表示"内部/私有"(约定) |
__dunder | __init__, __str__ | Python 特殊方法(双下划线) |
# 1.2.2 数字类型:int、float、complex
Python 有三种数值类型——先看最核心的:
# int(整数)
Python 的 int 没有大小限制(理论上是无限精度的):
# 普通整数
a = 42
b = -10
c = 0
# 也可以用不同的进制表示
hex_val = 0xFF # 十六进制:255
oct_val = 0o77 # 八进制:63
bin_val = 0b1010 # 二进制:10
# Python 的大整数自动扩展(不需要像 C 那样用 long long)
big = 2 ** 100 # 1267650600228229401496703205376
print(big) # 完全正确——没有溢出!
2
3
4
5
6
7
8
9
10
11
12
13
🔑 对比 C/C++:
// C 的整数有固定宽度,会溢出
int x = 2147483647; // 2^31 - 1,32 位有符号最大值
x = x + 1; // 溢出!变成 -2147483648(未定义行为)
2
3
Python 不需要担心溢出——这是大数据和科学计算极其重要的特性。
# float(浮点数)
Python 的 float 对应 C 的 double(64 位 IEEE 754):
pi = 3.14159
e = 2.71828
sci = 1.5e-3 # 科学计数法:0.0015
# 浮点数精度问题(所有语言都有)
print(0.1 + 0.2) # 0.30000000000000004(不是精确的 0.3!)
print(0.1 + 0.2 == 0.3) # False
# 解决方案:用 math.isclose() 或 decimal 模块
import math
print(math.isclose(0.1 + 0.2, 0.3)) # True
2
3
4
5
6
7
8
9
10
11
⚠️ 浮点数不能做相等比较——这是 IEEE 754 的固有问题,不是 Python bug。
# complex(复数)
Python 原生支持复数——数学和工程领域的杀手特性:
z = 3 + 4j # 实部 3,虚部 4
print(z.real) # 3.0
print(z.imag) # 4.0
print(abs(z)) # 5.0(模长 = sqrt(3² + 4²))
print(z.conjugate()) # (3-4j)
2
3
4
5
# 数值运算常用函数
# 内置函数(不需要 import)
print(abs(-10)) # 10(绝对值)
print(round(3.1415, 2)) # 3.14(四舍五入到 2 位小数)
print(pow(2, 10)) # 1024(等价于 2 ** 10)
print(divmod(17, 5)) # (3, 2)(商和余数同时返回)
print(max(3, 7, 2, 9)) # 9
print(min(3, 7, 2, 9)) # 2
print(sum([1, 2, 3, 4]))# 10
# math 模块(需要 import)
import math
print(math.sqrt(16)) # 4.0(平方根)
print(math.ceil(3.14)) # 4(向上取整)
print(math.floor(3.14)) # 3(向下取整)
print(math.factorial(5))# 120(阶乘)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 1.2.3 字符串类型:str
Python 的字符串是 不可变的 Unicode 序列——这是理解字符串一切行为的总钥匙。
# 四种定义方式
s1 = '单引号' # 最常用
s2 = "双引号" # 和单引号完全等价
s3 = '''可以跨行
的三引号''' # 保留换行
s4 = """也可以跨行
的""" # 同上
2
3
4
5
6
# 常用操作
s = "Hello, Python!"
# 索引(从 0 开始)
print(s[0]) # H
print(s[-1]) # !(负数从末尾开始)
# 切片 [start:end:step]
print(s[0:5]) # Hello(索引 0~4)
print(s[7:]) # Python!(从 7 到末尾)
print(s[:5]) # Hello(从开头到 4)
print(s[::-1]) # !nohtyP ,olleH(反转字符串)
print(s[::2]) # Hlo yhn(每隔一个取)
# 长度
print(len(s)) # 14
# 连接
print("Hello" + " " + "World") # Hello World
print("abc" * 3) # abcabcabc(重复)
# 成员检查
print("Python" in s) # True
print("Java" not in s) # True
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 字符串方法速查
s = " Hello, World! "
print(s.strip()) # "Hello, World!"(去首尾空格)
print(s.lower()) # " hello, world! "(全小写)
print(s.upper()) # " HELLO, WORLD! "(全大写)
print(s.replace("World", "Python")) # " Hello, Python! "
print(s.split(",")) # [' Hello', ' World! '](按逗号分割)
print(s.find("World")) # 9(查找子串位置,找不到返回 -1)
print(s.count("l")) # 3(统计 l 出现次数)
print(s.startswith(" He")) # True
print(s.endswith("! ")) # True
# 检查字符串类型
print("123".isdigit()) # True(全是数字)
print("abc".isalpha()) # True(全是字母)
print("abc123".isalnum())# True(全是字母或数字)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 字符串格式化:f-string(Python 3.6+)
这是 Python 最优雅的特性之一:
name = "张三"
age = 25
height = 1.75
# f-string:最推荐的方式
print(f"我叫{name},今年{age}岁,身高{height:.2f}米")
# 我叫张三,今年25岁,身高1.75米
# 嵌入表达式
print(f"明年我就{age + 1}岁了") # 明年我就26岁了
print(f"名字长度:{len(name)}") # 名字长度:2
# 对齐与填充
print(f"|{name:<10}|") # |张三 |(左对齐,10 宽度)
print(f"|{name:>10}|") # | 张三|(右对齐)
print(f"|{name:^10}|") # | 张三 |(居中)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 1.2.4 布尔类型:bool
Python 的 bool 是 int 的子类——True == 1、False == 0:
t = True
f = False
print(t == 1) # True(bool 是 int 的子类!)
print(f == 0) # True
print(t + t) # 2(可以参与算术运算)
print(isinstance(True, int)) # True
# 真值判定规则(Truthy / Falsy)
# 以下值被视为 False:
print(bool(0)) # False
print(bool(0.0)) # False
print(bool("")) # False(空字符串)
print(bool([])) # False(空列表)
print(bool(None)) # False
print(bool({})) # False(空字典)
# 其余所有值都是 True
print(bool(1)) # True
print(bool(-1)) # True(非零即真)
print(bool(" ")) # True(非空字符串)
print(bool([0])) # True(非空列表)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
⚠️ 新手最容易踩的坑:
if x:不是if x == True:——前者检查"Truthy",后者检查bool身份。永远写if x:或if x is not None:。
# 1.2.5 None 类型
None 是 Python 的"空值"——相当于 C 的 NULL、Java 的 null:
x = None
print(x) # None
print(type(x)) # <class 'NoneType'>
print(x is None) # True(用 is 比较,不是 ==!)
# 典型用法:函数没有显式 return 时默认返回 None
def no_return():
pass
print(no_return()) # None
2
3
4
5
6
7
8
9
10
🔑 为什么要用 is None 而不是 == None? 因为 None 是单例——整个 Python 进程只有一个 None 对象。is 是身份比较(同一对象),比 ==(值比较)更语义正确且更快。
# 1.2.6 类型检查与转换
# 类型检查
x = 42
print(type(x)) # <class 'int'>
print(type(x) == int) # True
print(isinstance(x, int)) # True(推荐:支持继承检查)
# isinstance 比 type() == 更好——能识别子类
print(isinstance(True, int)) # True(bool 是 int 子类)
print(type(True) == int) # False
2
3
4
5
6
7
8
# 类型转换
# int()
print(int(3.14)) # 3(截断小数)
print(int("100")) # 100
# print(int("abc")) # ValueError——不能转
# float()
print(float(42)) # 42.0
print(float("3.14")) # 3.14
# str()
print(str(42)) # "42"
print(str(True)) # "True"
# bool()
print(bool(0)) # False
print(bool("hello")) # True
# 二进制/八进制/十六进制
print(bin(10)) # '0b1010'
print(oct(10)) # '0o12'
print(hex(255)) # '0xff'
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 1.2.7 变量的底层:引用与对象
这是 Python 新手最迷惑、也最重要的底层机制——变量不是盒子,是指针:
a = [1, 2, 3]
b = a # b 指向同一个列表!不是复制!
b.append(4) # 修改 b 指向的列表
print(a) # [1, 2, 3, 4]——a 也被改了!
2
3
4
5
图解:
a ──→ [1, 2, 3, 4]
b ──→ ↑(同一个对象)
# 而不可变对象(int、str、tuple)的行为"看起来"像值:
x = 10
y = x # y 指向同一个整数对象 10
y = 20 # y 指向新的整数对象 20——原对象不变
print(x) # 10(x 不受影响)
2
3
4
5
6
7
8
🔑 可变 vs 不可变对象:
| 不可变(赋新值时创建新对象) | 可变(原地修改) |
|---|---|
int, float, complex | list |
str, bytes | dict |
tuple | set |
bool, None | 自定义类实例(默认) |
验证身份的利器——id():
a = [1, 2]
b = a
c = [1, 2]
print(id(a)) # 140234567890(某个内存地址)
print(id(b)) # 140234567890(相同!b 就是 a 的别名)
print(id(c)) # 140234567999(不同!c 是新创建的对象)
print(a is b) # True(同一对象)
print(a is c) # False(不同对象)
print(a == c) # True(值相等!)
2
3
4
5
6
7
8
9
10
11
📌
isvs==:is比较的是身份(同一内存地址),==比较的是值。除None外,绝大多数场景你该用==。
故意造 bug:浅拷贝的陷阱
# 变量共享对象:修改一方影响另一方
original = [1, 2, 3]
backup = original # ❌ 不是备份!是指向同一个列表
backup.append(999)
print(original) # [1, 2, 3, 999]——"原数据被污染了!"
# ✅ 真正的复制
real_backup = original.copy() # 或 original[:]
real_backup.append(888)
print(original) # [1, 2, 3, 999]——原数据不受影响
2
3
4
5
6
7
8
9
10
# 1.2.8 综合案例与思考
综合案例:学生成绩统计工具
"""学生成绩统计——展示变量、数据类型、运算的综合运用"""
# 1. 学生信息(多种类型)
name = "李明"
student_id = 2024001
scores = [85, 92, 78, 90, 88] # 5 门课的成绩
is_active = True
# 2. 计算统计值
total = sum(scores) # 总分
average = total / len(scores) # 平均分
max_score = max(scores) # 最高分
min_score = min(scores) # 最低分
passed_all = all(s >= 60 for s in scores) # 是否全部及格
# 3. 格式化输出
print("=" * 50)
print(f"{'学生成绩报告':^50}")
print("=" * 50)
print(f"姓名:{name}")
print(f"学号:{student_id}")
print(f"在校状态:{'在读' if is_active else '已离校'}")
print(f"各科成绩:{scores}")
print("-" * 50)
print(f"总 分:{total}(满分 {len(scores) * 100})")
print(f"平均分:{average:.2f}")
print(f"最高分:{max_score} 最低分:{min_score}")
print(f"全科及格:{passed_all}")
print(f"成绩等级:{chr(ord('A') + (4 - int((average - 60) / 10)))}") # 简单等级计算
# 4. 数据验证——类型信息
print("-" * 50)
print(f"总分类型:{type(total).__name__}(值={total})")
print(f"平均分类型:{type(average).__name__}")
print(f"成绩列表长度:{len(scores)} 类型:{type(scores).__name__}")
# 5. 布尔运算示例
print("-" * 50)
has_excellent = any(s >= 90 for s in scores)
has_failed = any(s < 60 for s in scores)
print(f"有优秀成绩(≥90):{has_excellent}")
print(f"有不及格(<60):{has_failed}")
print(f"全是优秀:{all(s >= 90 for s in scores)}")
print("=" * 50)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
运行输出:
==================================================
学生成绩报告
==================================================
姓名:李明
学号:2024001
在校状态:在读
各科成绩:[85, 92, 78, 90, 88]
--------------------------------------------------
总 分:433(满分 500)
平均分:86.60
最高分:92 最低分:78
全科及格:True
成绩等级:B
--------------------------------------------------
总分类型:int(值=433)
平均分类型:float
成绩列表长度:5 类型:list
--------------------------------------------------
有优秀成绩(≥90):True
有不及格(<60):False
全是优秀:False
==================================================
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
案例知识融合:这个案例在一个真实场景中串联了全部知识点——变量(name、is_active)、int/float/str/bool 四种基本类型、列表及 sum/max/min/len 计算、all/any 布尔聚合、算术/比较/逻辑运算、f-string 格式化、type() 类型检查——30 行代码还原了真实的数据处理流程。
思考题:
average = total / len(scores)这里为什么结果是float而不是int?如果你想要整数平均分怎么办?all(s >= 60 for s in scores)中的s >= 60 for s in scores是什么类型?它是如何工作的?(提示:生成器表达式)- 如果
scores列表有 10000 个元素,all()和any()有没有短路行为?它们何时停止遍历?
# 1.3 运算符与表达式
# 1.3.1 算术运算符
a, b = 10, 3
print(a + b) # 13(加法)
print(a - b) # 7(减法)
print(a * b) # 30(乘法)
print(a / b) # 3.3333333333333335(除法——总是返回 float!)
print(a // b) # 3(整除——向下取整)
print(a % b) # 1(取模——余数)
print(a ** b) # 1000(指数——a 的 b 次方)
print(-a) # -10(取负)
2
3
4
5
6
7
8
9
10
⚠️ 特别注意:Python 的 / 永远返回 float,不像 C 语言 int/int = int。
# C 语言:10 / 3 = 3(整数除法,抛弃小数)
# Python:10 / 3 = 3.333...(真除法,总是 float)
# 要整数结果用 //
print(10 // 3) # 3
print(-10 // 3) # -4(向下取整!不是 C 的截断取整 -3!)
2
3
4
5
# 1.3.2 比较运算符
x, y = 5, 10
print(x == y) # False
print(x != y) # True
print(x < y) # True
print(x > y) # False
print(x <= 5) # True
print(x >= 5) # True
# 链式比较(Python 独有!)
age = 25
print(18 <= age <= 30) # True(等价于 18 <= age and age <= 30)
print(0 < 10 < 20 < 30) # True
print(1 < 5 > 3 < 8) # True(不建议这样写——可读性差)
# 字符串比较——逐字符按 Unicode 码点比较
print("apple" < "banana") # True(a < b)
print("abc" < "abz") # True(前两字符相同,c < z)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 1.3.3 逻辑运算符
# and / or / not(Python 用的是英文单词,不是 && / || / !)
a, b = True, False
print(a and b) # False(与)
print(a or b) # True(或)
print(not a) # False(非)
# 短路求值——这是精髓!
def true_func():
print("true_func 被调用了!")
return True
def false_func():
print("false_func 被调用了!")
return False
print("--- 测试短路:or ---")
result = true_func() or false_func()
# true_func 返回 True 后,or 已经确定结果——false_func 不会执行!
print(f"结果:{result}")
# 输出:
# true_func 被调用了!
# 结果:True
print("\n--- 测试短路:and ---")
result = false_func() and true_func()
# false_func 返回 False 后,and 已经确定结果——true_func 不会执行!
print(f"结果:{result}")
# 输出:
# false_func 被调用了!
# 结果:False
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
🔑 短路求值的实战技巧:
# 技巧一:用 or 提供默认值
name = input_name or "未命名" # 如果 input_name 为空字符串,用"未命名"
# 技巧二:用 and 做前置条件检查
items and items[0] # 如果 items 非空,取第一个元素;否则返回空列表
2
3
4
5
# 1.3.4 赋值运算符
x = 10 # 基本赋值
x += 5 # x = x + 5 → 15
x -= 3 # x = x - 3 → 12
x *= 2 # x = x * 2 → 24
x /= 4 # x = x / 4 → 6.0
x //= 2 # x = x // 2 → 3.0
x **= 3 # x = x ** 3 → 27.0
x %= 7 # x = x % 7 → 6.0
# 多重赋值(Python 特色!)
a, b = 1, 2 # 同时给多个变量赋值
a, b = b, a # 交换变量——不需要临时变量!
# 解包赋值
x, y, z = [10, 20, 30] # 列表解包
first, *rest = [1, 2, 3, 4, 5] # first=1, rest=[2,3,4,5]
*begin, last = [1, 2, 3, 4, 5] # begin=[1,2,3,4], last=5
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 1.3.5 位运算符
a, b = 0b1010, 0b1100 # 10 和 12
print(bin(a & b)) # 0b1000(按位与:1000 = 8)
print(bin(a | b)) # 0b1110(按位或:1110 = 14)
print(bin(a ^ b)) # 0b0110(按位异或:0110 = 6)
print(bin(~a)) # -0b1011(按位取反:Python 无限精度,结果含符号位)
print(bin(a << 2)) # 0b101000(左移 2 位:101000 = 40,等价于 ×4)
print(bin(a >> 1)) # 0b101(右移 1 位:101 = 5,等价于 ÷2)
2
3
4
5
6
7
8
# 1.3.6 成员与身份运算符
# 成员运算符(in / not in)
fruits = ["苹果", "香蕉", "橘子"]
print("苹果" in fruits) # True
print("西瓜" not in fruits) # True
print("py" in "python") # True(字符串也可用 in)
# 身份运算符(is / is not)
a = [1, 2, 3]
b = a
c = [1, 2, 3]
print(a is b) # True(同一对象)
print(a is c) # False(不同对象,虽然值相等)
print(a is not c) # True
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1.3.7 运算符优先级
从高到低——不用死记,该加括号就加括号:
# 优先级从高到低:
# () 括号(最高)
# ** 指数
# +x, -x, ~x 一元
# *, /, //, % 乘除
# +, - 加减
# <<, >> 位移
# & 位与
# ^ 位异或
# | 位或
# ==, !=, <, <=, >, >=, is, in 比较
# not 逻辑非
# and 逻辑与
# or 逻辑或(最低)
2
3
4
5
6
7
8
9
10
11
12
13
14
不用死记的示范:
# 不要写:
result = a + b * c ** d / e % f # 没人看得懂
# 要写:
result = a + (b * (c ** d) / e) % f # 一目了然——多余的括号不花钱
2
3
4
5
📌 金科玉律:优先级表你可以永远不背——但永远要加括号让意图明确。代码是写给人看的。
# 1.3.8 综合案例与思考
综合案例:BMI 计算器——运算符全家桶
"""
BMI 计算器——展示算术、比较、逻辑、赋值、字符串格式化的综合运用
BMI = 体重(kg) / 身高(m)²
"""
# 1. 输入
weight = 70.5 # 公斤
height = 1.75 # 米
age = 28
is_male = True
# 2. 计算 BMI(算术运算符)
bmi = weight / (height ** 2)
# 3. 判定等级(比较运算符 + 逻辑运算符)
is_underweight = bmi < 18.5
is_normal = 18.5 <= bmi < 24.0 # 链式比较!
is_overweight = 24.0 <= bmi < 28.0
is_obese = bmi >= 28.0
# 4. 综合健康判定
is_healthy = is_normal and (age < 60 or bmi < 25)
needs_warning = is_obese or (is_overweight and age > 45)
# 5. 输出(f-string 格式化 + 三元表达式)
print("=" * 45)
print(f"{'BMI 健康评估报告':^45}")
print("=" * 45)
print(f"身高:{height:.2f} m")
print(f"体重:{weight:.1f} kg")
print(f"年龄:{age} 岁 性别:{'男' if is_male else '女'}")
print("-" * 45)
print(f"BMI 指数:{bmi:.1f}")
print(f"体重不足:{is_underweight}")
print(f"体重正常:{is_normal}")
print(f"体重过重:{is_overweight}")
print(f"肥 胖:{is_obese}")
print("-" * 45)
print(f"健康评估:{'✅ 健康' if is_healthy else '⚠️ 需要关注'}")
# 6. 位运算彩蛋——用位运算存储多个标签
# bit 0: 体重不足, bit 1: 正常, bit 2: 过重, bit 3: 肥胖
flags = (
(1 if is_underweight else 0) |
(2 if is_normal else 0) |
(4 if is_overweight else 0) |
(8 if is_obese else 0)
)
print(f"标签位掩码:{flags:04b}(十进制={flags})")
print(f"是否为'正常'标签:{bool(flags & 2)}")
# 7. 建议输出
print("-" * 45)
suggestions = []
if is_underweight:
suggestions.append("建议增加营养摄入和力量训练")
elif is_normal:
suggestions.append("继续保持当前生活方式")
else:
suggestions.append("建议控制饮食并增加有氧运动")
for i, s in enumerate(suggestions, 1):
print(f"建议 {i}:{s}")
print("=" * 45)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
运行输出:
=============================================
BMI 健康评估报告
=============================================
身高:1.75 m
体重:70.5 kg
年龄:28 岁 性别:男
---------------------------------------------
BMI 指数:23.0
体重不足:False
体重正常:True
体重过重:False
肥 胖:False
---------------------------------------------
健康评估:✅ 健康
标签位掩码:0010(十进制=2)
是否为'正常'标签:True
---------------------------------------------
建议 1:继续保持当前生活方式
=============================================
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
案例知识融合:这个案例覆盖了 1.2-1.3 节的几乎全部知识点——浮点运算、幂运算 **、链式比较 18.5 <= bmi < 24.0、短路求值 age < 60 or bmi < 25、赋值简化 +=、位运算标签位的组合与检验、f-string 格式化、三元表达式——50 行代码完成了一个实用小工具。
思考题:
bmi = weight / (height ** 2)中的括号可以省略吗?如果写成weight / height ** 2结果会不同吗?为什么?18.5 <= bmi < 24.0在 C/C++ 中合法吗?C 语言中相同语义该怎么写?- 标签位
flags & 2为什么返回的是2而不是True?bool(2)和2 == True为什么结果不同?
# 1.4 输入输出基础
# 1.4.1 print() 输出
print() 远比看起来强大:
# 基本用法
print("Hello") # 默认末尾换行
print("World")
# sep 参数:分隔符(默认空格)
print("A", "B", "C", sep="-") # A-B-C
print("2025", "06", "07", sep="/") # 2025/06/07
# end 参数:末尾字符(默认换行符)
print("Loading", end="...")
print("Done!") # Loading...Done!
# 输出到文件
with open("output.txt", "w") as f:
print("写入文件", file=f)
# 打印复杂对象
data = {"name": "张三", "scores": [90, 85, 92]}
print(data) # {'name': '张三', 'scores': [90, 85, 92]}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 1.4.2 input() 输入
input() 永远返回字符串——这是新手最容易踩的坑:
name = input("请输入姓名:")
print(f"你好,{name}!")
# ⚠️ 数字输入需要手动转换!
age_str = input("请输入年龄:")
age = int(age_str) # 必须手动转
# 或者一行完成:
# age = int(input("请输入年龄:"))
# 防御式输入(处理非数字输入)
raw = input("请输入一个数字:")
try:
number = float(raw)
print(f"你输入的数字是:{number}")
print(f"它的平方是:{number ** 2}")
except ValueError:
print(f"'{raw}' 不是有效的数字!")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1.4.3 综合案例与思考
综合案例:交互式计算器
"""交互式计算器——展示 print/input 的综合运用"""
print("=" * 40)
print(" Python 简易计算器")
print("=" * 40)
# 操作提示
print("\n支持的操作:")
print(" + 加法 - 减法 * 乘法 / 除法")
print(" ** 幂运算 // 整除 % 取模")
print(" (输入 'q' 退出)\n")
while True:
# 输入第一个数字
raw = input("第一个数字:").strip()
if raw.lower() == 'q':
print("再见!")
break
try:
num1 = float(raw)
except ValueError:
print(f"❌ '{raw}' 不是有效的数字,请重新输入\n")
continue
# 输入运算符
op = input("运算符:").strip()
if op.lower() == 'q':
print("再见!")
break
if op not in ('+', '-', '*', '/', '**', '//', '%'):
print(f"❌ 不支持的运算符:'{op}'\n")
continue
# 输入第二个数字
raw = input("第二个数字:").strip()
if raw.lower() == 'q':
print("再见!")
break
try:
num2 = float(raw)
except ValueError:
print(f"❌ '{raw}' 不是有效的数字,请重新输入\n")
continue
# 执行运算
try:
if op == '+': result = num1 + num2
elif op == '-': result = num1 - num2
elif op == '*': result = num1 * num2
elif op == '/': result = num1 / num2
elif op == '**':result = num1 ** num2
elif op == '//':
if num2 == 0:
print("❌ 不能被零整除\n")
continue
result = num1 // num2
elif op == '%':
if num2 == 0:
print("❌ 不能对零取模\n")
continue
result = num1 % num2
else:
result = None
# 格式化输出(整数不显示小数位)
display = int(result) if result == int(result) else result
print(f"✅ {num1} {op} {num2} = {display}\n")
except ZeroDivisionError:
print("❌ 不能除以零!\n")
except OverflowError:
print("❌ 数字太大,溢出!\n")
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
交互演示:
========================================
Python 简易计算器
========================================
支持的操作:
+ 加法 - 减法 * 乘法 / 除法
** 幂运算 // 整除 % 取模
(输入 'q' 退出)
第一个数字:10
运算符:**
第二个数字:3
✅ 10.0 ** 3.0 = 1000
第一个数字:17
运算符:/
第二个数字:5
✅ 17.0 / 5.0 = 3.4
第一个数字:17
运算符://
第二个数字:5
✅ 17.0 // 5.0 = 3
第一个数字:q
再见!
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
案例知识融合:这个案例完整展示了 input() 的类型转换陷阱、try/except 防御式编程、if/elif 分支控制、浮点计算、除零保护——虽然只有 60 多行,但已经是一个可用且健壮的小工具了。
思考题:
input()永远返回字符串——这是设计缺陷还是有意为之?如果input()像 C 的scanf()那样自动转换类型会有什么问题?- 本案例用
result == int(result)判断结果是否是整数——这个判断有精度问题吗?对于1.0000000000000001会发生什么? - 如果要支持
(1 + 2) * 3这种带括号的复杂表达式,你需要引入什么概念?(提示:这是我们综合案例 §07 的核心——词法分析 + 语法分析)
# 1.5 新手陷阱
| # | 陷阱 | 说明 |
|---|---|---|
| 1 | input() 返回字符串 | age = input() 后 age + 1 报 TypeError——必须 int() 转换 |
| 2 | 变量是引用,不是值 | b = a 后修改 b 可能影响 a(可变对象)——想复制用 .copy() |
| 3 | / 永远返回 float | 10 / 2 结果是 5.0 不是 5——想要整数用 // |
| 4 | == 和 is 混淆 | [1,2] == [1,2] 是 True,但 [1,2] is [1,2] 是 False——除 None 外都用 == |
| 5 | 缩进混用 Tab 和空格 | IndentationError: unexpected indent——统一用 4 空格,IDE 设置"Tab 转空格" |
# 1.6 综合思考题
动态类型的代价:Python 不用声明变量类型——这让代码极简。但在一个 10 万行的大项目中,你怎么知道一个函数
def process(data)的data参数应该传什么?Python 3.5+ 引入了类型注解(Type Hints)——def process(data: list[int]) -> dict:,这对大项目有多大帮助?类型注解会被运行时强制执行吗?不可变对象的内存共享:Python 对小整数(-5 ~ 256)做了对象缓存:
a, b = 256, 256 print(a is b) # True(同一个对象,被缓存了) a, b = 257, 257 print(a is b) # False(不同对象!)1
2
3
4这种"整数缓存"的机制是什么?为什么边界是 -5 ~ 256 而不是其他范围?这对
is比较有什么影响?字符串不可变的深层原因:
s = "hello" s[0] = "H" # TypeError! 字符串不可修改 s = "H" + s[1:] # 正确方式:创建新字符串1
2
3为什么 Python 选择让字符串不可变?如果字符串可变(如 C 的
char[]),字典的 key 还能安全使用字符串吗?不可变对象在并发编程中有什么天然优势?Python 的"一切皆对象":C 语言中
int x = 5直接分配 4 字节内存存值。Python 中x = 5则创建一个PyLongObject结构体(内含类型指针、引用计数、值),大约占用 28 字节。这种"胖对象"设计让 Python 灵活——但也让它的内存占用远超 C。你觉得这种设计在什么场景下是合适的?什么场景下不合适?f-string 的"魔法":
f"1 + 2 = {1 + 2}"为什么能直接内嵌1 + 2这个表达式?它在底层是如何被解析和执行的?开发一个简单的 f-string 解析器需要具备什么知识?