chore: clean repository history

This commit is contained in:
xixu-me committed 2026-06-05 21:13:11 +08:00
commit e1fb2929c0
661 files changed
+167488

No files matched your search

@@ -0,0 +1,352 @@
---
doc_type: short
full_text: sources/07_Objects.md
---
# 07_Objects 总结
本文介绍 Python 的内部对象模型,重点说明赋值、引用、对象身份、浅拷贝与深拷贝、类型检查,以及“一切皆对象”的含义。核心思想是:Python 变量只是名字,值才是真正的对象;赋值不会复制对象,只会复制引用。相关主题可连接到 [[concepts/Python-对象模型]]、[[concepts/可变性与引用]]、[[concepts/Python-拷贝语义]]、一等对象。
## 赋值不是复制
Python 中许多操作本质上都是“赋值”或“存储引用”:
```python
a = value
s[n] = value
s.append(value)
d['key'] = value
```
这些操作都不会复制被赋的值,而只是复制对象引用。也就是说,多个变量或容器元素可能指向同一个底层对象。
例如:
```python
a = [1, 2, 3]
b = a
c = [a, b]
```
这里实际上只有一个列表对象 `[1, 2, 3]`,但有多个引用指向它:`a`、`b`、`c[0]`、`c[1]`。如果修改列表:
```python
a.append(999)
```
那么通过 `a`、`b`、`c` 看到的内容都会变化。这是 Python 中 [[concepts/可变性与引用]] 的典型陷阱。
## 重新赋值不会覆盖旧对象
重新赋值不会修改旧对象本身,而是让变量名绑定到另一个对象:
```python
a = [1, 2, 3]
b = a
a = [4, 5, 6]
```
此时:
```python
print(a) # [4, 5, 6]
print(b) # [1, 2, 3]
```
关键原则:**变量是名字,不是内存位置。**
这也是理解 [[concepts/Python-对象模型]] 的基础。
## 共享可变对象的风险
如果不了解引用共享,程序中很容易出现意外的数据污染:以为自己在修改“私有副本”,实际上却修改了其他代码也在使用的同一个对象。
这也是 Python 中 `int`、`float`、`str` 等基础类型设计为不可变对象的重要原因之一:不可变对象即使被多个引用共享,也不会因为原地修改而互相影响。
## 对象身份与 `is`
`is` 用于判断两个变量是否引用同一个对象:
```python
a = [1, 2, 3]
b = a
a is b # True
```
对象身份可以通过 `id()` 查看:
```python
id(a)
id(b)
```
如果两个变量指向同一个对象,它们的 `id()` 相同。
但通常情况下,比较对象内容应使用 `==`,而不是 `is`:
```python
a = [1, 2, 3]
b = a
c = [1, 2, 3]
a is b # True
a is c # False
a == c # True
```
`is` 比较身份,`==` 比较值。这个区别是 [[concepts/Python-对象模型]] 中的重要概念。
## 浅拷贝
列表和字典可以创建拷贝,例如:
```python
a = [2, 3, [100, 101], 4]
b = list(a)
```
此时 `a` 和 `b` 是两个不同的外层列表:
```python
a is b # False
```
但其中的内部对象仍然被共享:
```python
a[2].append(102)
b[2] # [100, 101, 102]
a[2] is b[2] # True
```
这种只复制外层容器、不递归复制内部对象的行为称为浅拷贝。它属于 [[concepts/Python-拷贝语义]] 的核心内容。
## 深拷贝
如果需要复制对象以及它包含的所有嵌套对象,可以使用 `copy.deepcopy()`:
```python
import copy
a = [2, 3, [100, 101], 4]
b = copy.deepcopy(a)
a[2].append(102)
b[2] # [100, 101]
a[2] is b[2] # False
```
深拷贝适用于需要完全隔离嵌套可变结构的场景,但也可能带来额外开销和复杂性。
## 名字、值与类型
Python 中变量名本身没有类型,类型属于对象值:
```python
a = 42
b = 'Hello World'
type(a) # int
type(b) # str
```
`type()` 可以查看对象类型。类型名通常也可以作为构造或转换函数使用,例如 `int()`、`str()`、`float()`。
## 类型检查
可以使用 `isinstance()` 判断对象是否属于某种类型:
```python
if isinstance(a, list):
print('a is a list')
```
也可以检查是否属于多个类型之一:
```python
if isinstance(a, (list, tuple)):
print('a is a list or tuple')
```
不过,文章提醒不要过度使用类型检查。过多类型判断会增加代码复杂度。通常只有在防止常见误用时才值得加入类型检查。
## 一切皆对象
Python 中数字、字符串、列表、函数、异常、类、实例、模块等都是对象。所有可以被命名的东西,都可以作为数据传递、放入容器、作为参数使用。
这体现了 Python 的 一等对象 特性。
例如:
```python
import math
items = [abs, math, ValueError]
items[0](-45) # abs(-45)
items[1].sqrt(2) # math.sqrt(2)
```
甚至异常类也可以放在列表中并用于 `except`:
```python
try:
x = int('not a number')
except items[2]:
print('Failed!')
```
这种能力很强大,但也需要谨慎使用。能这样写并不代表总应该这样写。
## 练习 2.24:一等数据与类型转换函数
练习展示了如何利用“一切皆对象”的特性,把类型转换函数放入列表:
```python
types = [str, int, float]
```
读取 CSV 行后,原始数据都是字符串:
```python
row = ['AA', '100', '32.20']
```
可以用对应的转换函数处理字段:
```python
types[1](row[1]) # int('100') -> 100
types[2](row[2]) # float('32.20') -> 32.2
```
通过 `zip()` 将转换函数与字段配对:
```python
list(zip(types, row))
```
得到类似:
```python
[(str, 'AA'), (int, '100'), (float, '32.20')]
```
然后可统一转换:
```python
converted = []
for func, val in zip(types, row):
converted.append(func(val))
```
也可以写成列表推导式:
```python
converted = [func(val) for func, val in zip(types, row)]
```
得到:
```python
['AA', 100, 32.2]
```
这一部分连接了 [[concepts/函数作为对象]]、[[concepts/列表推导式]] 与 [[concepts/数据清洗与类型转换]]。
## 练习 2.25:构造字典
将列名与转换后的值配对,可以用 `dict()` 创建字典:
```python
headers = ['name', 'shares', 'price']
converted = ['AA', 100, 32.2]
dict(zip(headers, converted))
```
结果:
```python
{'name': 'AA', 'shares': 100, 'price': 32.2}
```
也可以使用字典推导式一步完成转换和建表:
```python
{name: func(val) for name, func, val in zip(headers, types, row)}
```
这展示了如何把 CSV 行转换为结构化记录,是 [[concepts/数据清洗与类型转换]] 的实用模式。
## 练习 2.26:更大的应用图景
同样技巧可推广到其他面向列的数据文件。例如读取股票数据:
```python
headers = ['name', 'price', 'date', 'time', 'change', 'open', 'high', 'low', 'volume']
row = ['AA', '39.48', '6/11/2007', '9:36am', '-0.18', '39.67', '39.69', '39.45', '181800']
```
定义每列对应的转换函数:
```python
types = [str, float, str, str, float, float, float, float, int]
```
然后转换并构造记录:
```python
converted = [func(val) for func, val in zip(types, row)]
record = dict(zip(headers, converted))
```
得到的 `record` 可以通过字段名访问:
```python
record['name']
record['price']
```
文末还提出扩展问题:如何将 `date` 字段解析为 `(6, 11, 2007)` 这样的元组。这暗示可以自定义转换函数,而不仅限于内置类型函数。
## 核心要点
- 赋值不会复制对象,只会复制引用。
- 变量是名字,不是内存位置。
- 修改可变对象会影响所有共享该对象的引用。
- `is` 比较对象身份,`==` 比较对象值。
- 浅拷贝只复制外层容器,内部对象仍共享。
- 深拷贝会递归复制嵌套对象。
- 类型属于值,不属于变量名。
- `isinstance()` 可用于类型检查,但不应滥用。
- Python 中一切皆对象,函数、模块、异常也能作为普通数据使用。
- 一等对象特性可用于构建通用的数据转换流程。
## 相关概念
- [[concepts/Python-对象模型]]
- [[concepts/可变性与引用]]
- [[concepts/Python-拷贝语义]]
- 一等对象
- [[concepts/列表推导式]]
- [[concepts/数据清洗与类型转换]]
## Related Concepts
- [[concepts/Python-拷贝语义]]
- [[concepts/可变性与引用]]
- [[concepts/列表与序列]]
- [[concepts/Python-运算符与表达式]]
- [[concepts/对象身份与相等性]]
- [[concepts/浅拷贝与深拷贝]]
- [[concepts/Python-对象模型]]
- [[concepts/Python-可变对象]]
- [[concepts/Python-不可变对象]]
- [[concepts/变量与数据类型]]
- [[concepts/函数作为对象]]
- [[concepts/CSV-数据处理]]
- [[concepts/字典与数据建模]]
- [[concepts/Python-容器]]
- [[concepts/元组与解包]]
- [[concepts/鸭子类型]]
- [[concepts/文件读写]]
- [[concepts/模块与-import]]
- [[concepts/浮点数精度]]