学习路径:Python_AIGC · Day 2
理解 Python 的「变量」本质是标签,以及核心数据类型的正确使用姿势。
1️⃣ 变量本质:名字贴在对象上
与其他语言不同,Python 变量不是盒子,是标签。
python
a = [1, 2, 3]
b = a # b 和 a 指向同一个 list 对象
b.append(4)
print(a) # [1, 2, 3, 4] --- a 也被改了!
🖼️ 内存模型 :a 和 b 都是贴在 [1,2,3] 对象上的标签。修改对象内容,所有标签都看到变化。
id() --- 对象的身份证
每个 Python 对象创建后都有一个唯一的身份标识,id() 返回的就是这个值。在 CPython 里就是对象的内存地址。
python
a = [1, 2, 3]
b = a
c = [1, 2, 3]
print(id(a)) # 139824567890123
print(id(b)) # 139824567890123 --- b 是 a 的标签,同一对象
print(id(c)) # 139824568901234 --- c 是新对象,地址不同
# is 运算符底层就是比较 id()
print(b is a) # True → id(b) == id(a)
print(c is a) # False → id(c) != id(a)
print(c == a) # True → 值相等(逐元素比较)
小整数缓存 --- 实现细节别依赖
CPython 启动时会预创建 -5 到 256(含边界 )的小整数对象并复用。但这个行为在交互式环境(REPL)和函数/代码块内表现不一致:
python
# REPL 中逐行输入:
x = 256
y = 256
print(x is y) # True --- 在缓存范围内
x = 1000
y = 1000
print(x is y) # False --- 超出缓存,每行独立编译,不同对象
print(x == y) # True --- 值相等
# 但写在同一个函数/代码块里:
def test():
a = 1000
b = 1000
print(a is b) # ⚠️ 可能是 True!CPython 常量折叠优化
test()
结论:永远不要用 is 比较整数或字符串的值。is 只该用在一个场景------
python
# ✅ 唯一正确的 is 用法:判 None
result = some_function()
if result is None: # ✅ 正确,None 是全局单例
pass
if result == None: # ❌ 不要这样写,虽然结果一样但语义不对
pass
2️⃣ 核心类型速览
| 类型 | 可变 | 示例 | 说明 |
|---|---|---|---|
int |
❌ | 42, 0xFF, 0b1010 |
任意精度 |
float |
❌ | 3.14, 1e-5 |
双精度浮点 |
str |
❌ | "hello", f"val={x}" |
Unicode 字符串 |
bool |
❌ | True, False |
isinstance(True, int) → True |
NoneType |
❌ | None |
单例,用 is 判断 |
bytes |
❌ | b"abc", bytes([65,66]) |
原始字节 |
list |
✅ | [1, 2, 3] |
有序可变序列 |
dict |
✅ | {"a": 1} |
哈希表映射 |
set |
✅ | {1, 2, 3} |
无序不重复集合 |
tuple |
❌ | (1, 2), 1, 2 |
不可变序列 |
3️⃣ 类型转换 --- 显式大于隐式
python
# 显式转换(推荐)
s = "42"
n = int(s) # str → int
pi = float("3.14") # str → float
text = str(100) # int → str
# 容器转换
list("hello") # → ['h','e','l','l','o']
set([1,2,2,3]) # → {1, 2, 3}
tuple([1,2,3]) # → (1, 2, 3)
# 类型检查
isinstance(42, int) # True
isinstance("hi", (str, int)) # True --- 多个类型
隐式转换陷阱
python
# ❌ 隐式转换可能出问题
result = "The answer is " + 42 # TypeError!
result = "The answer is " + str(42) # ✅
# ⚠️ 浮点精度
0.1 + 0.2 == 0.3 # False! 0.30000000000000004
# 用 Decimal 处理精度敏感场景
from decimal import Decimal
Decimal("0.1") + Decimal("0.2") == Decimal("0.3") # True
4️⃣ 深浅拷贝 --- 区分复制标签 vs 复制对象
python
import copy
original = [[1, 2], [3, 4]]
# ❌ 只是拷贝标签
wrong = original
# 浅拷贝:新容器,但元素是同一引用
shallow = copy.copy(original)
shallow[0].append(99)
print(original) # [[1, 2, 99], [3, 4]] --- 内层也变了!
# 深拷贝:递归复制所有对象
deep = copy.deepcopy(original)
deep[0].append(999)
print(original) # [[1, 2, 99], [3, 4]] --- 不受影响
常用数据类型的浅拷贝方式
python
# list
a.copy() # 或 a[:]
# dict
b.copy() # 或 dict(b)
# set
c.copy() # 或 set(c)
5️⃣ 实用技巧
f-string 格式化
python
name = "Python"
ver = 3.12
print(f"{name} {ver}") # Python 3.12
print(f"{name:>10}") # 右对齐: Python
print(f"{ver:.2f}") # 3.12
print(f"{1024:b}") # 二进制:10000000000
print(f"{1024:#x}") # 十六进制:0x400
动态类型但静态检查
python
# Python 动态类型
def add(a, b):
return a + b
# 写类型标注供 mypy/pyright 检查
def add(a: int, b: int) -> int:
return a + b
⚠️ 避坑总结
| 坑 | 原因 | 解法 |
|---|---|---|
| 修改列表影响了其他变量 | 变量是标签 | 可变对象用 copy 或 deepcopy |
1.1 + 2.2 != 3.3 |
浮点精度 | 金额用 Decimal,比较用容差 |
is 比整数/字符串失灵 |
缓存/常量折叠不可靠 | 数值和字符串只用 ==,is 只判 None |
| 函数默认参数是可变对象 | 默认值只创建一次 | 用 None + 函数内初始化 |
📝 今日小结
- ✅ 变量是标签,
==判值,is判身份 - ✅ 核心类型分类:可变 vs 不可变
- ✅ 显式类型转换,警惕浮点精度
- ✅ 深浅拷贝的区别和使用场景