第3章封面

pandas 代码 = Python 内功 + 表格思维。第 3 章补齐内功:四大容器、函数的进阶玩法、文件读写。这一章的每个知识点都会在后面几十处反复出现,值得慢读。

📦 四大容器:tuple / list / dict / set

Python四大容器

元组 tuple:一寸不可变

t = 1, 2, 3                  # 括号可省
nested = (1, (2, 3), [4, 5]) # 可嵌套
a, b, c = t                  # 解包:一行拆出所有值
first, *rest = [1, 2, 3, 4]  # 星号吃掉剩余:rest = [2, 3, 4]
t.count(2)                   # 统计出现次数

不可变是它的性格:不能增删改。但注意「不可变的是引用」——元组里的列表照样能 append。函数返回多个值(本质是返回一个元组)和变量交换 a, b = b, a 全靠解包这一手。

列表 list:万能收纳箱

lst = ["a", "b", "c"]
lst.append("d")        # 尾部加
lst.insert(1, "x")     # 指定位置插(慎用,O(n))
lst.pop(2)             # 弹出并返回
lst.remove("x")        # 按值删第一个
"b" in lst             # True,成员判断

lst2 = [7, 2, 5, 1, 3]
lst2.sort()            # 原地排序
lst2.sort(key=len)     # 或按函数返回值排序

切片是列表(也是 NumPy/pandas)的灵魂操作:

seq = [7, 2, 3, 7, 5, 6, 0, 1]
seq[1:5]        # [2, 3, 7, 5],左闭右开
seq[::2]        # [7, 3, 5, 0],隔一个取
seq[::-1]       # 反转
seq[1:5] = [8, 8, 8, 8]   # 切片赋值

⚠️ 切片是副本(列表上),和 NumPy 的视图规则不同,别混淆。

字典 dict:O(1) 的键值魔法

d1 = {"a": 1, "b": [1, 2, 3]}
d1["c"] = 5              # 新增/覆盖
d1.get("x", 0)           # 安全取值,不存在返回默认 0
d1.keys() / d1.values() / d1.items()

# 合并(Python 3.5+ 神语法)
d2 = {"b": 99, "d": 4}
{**d1, **d2}

两个经典套路:按值分组建多值字典,以及 zip 交替两列表成对:

from collections import defaultdict
groups = defaultdict(list)
for w in ["apple", "ant", "banana", "bat"]:
    groups[w[0]].append(w)     # {'a': ['apple','ant'], 'b': [...]}

letters = list("abcd"); nums = range(4)
dict(zip(letters, nums))         # {'a':0,'b':1,'c':2,'d':3}

集合 set:去重 + 数学运算

a = {1, 2, 3, 4, 5}
b = {3, 4, 5, 6, 7}
a & b     # 交集 {3,4,5}
a | b     # 并集
a - b     # 差集
a ^ b     # 对称差(只在一方出现)

set(list) 一行去重,比循环快得多。

🧰 内置序列函数与推导式

enumerate(带下标遍历)、sorted(返回新列表)、zip(平行遍历)、reversed(反向迭代)——四个瑞士军刀。

推导式是 Python 优雅的巅峰,允许条件与嵌套:

strings = ["a", "as", "bat", "car", "dove", "python"]
[x.upper() for x in strings if len(x) > 2]
# ['BAT', 'CAR', 'DOVE', 'PYTHON']

{val: i for i, val in enumerate(strings)}          # 字典推导式
lengths = {x: len(x) for x in strings}
unique = {len(x) for x in strings}                  # 集合推导式 {1,2,3,4,6}

all_data = [["John", "Emily"], ["Mike", "Mary"]]
names = [n for names in all_data for n in names if n.count("m") > 0]

pandas 源码和数据分析脚本里推导式无处不在,读懂它 = 读懂一半 Python 代码。

🧩 函数的进阶心法

函数皆对象——可以赋值、传参、存进字典:

def clean_str(s): return s.strip().title()
ops = {"clean": clean_str, "upper": str.upper}
ops["clean"]("  hello world ")   # 'Hello World'

lambda:匿名小函数,pandas 里天天见:

df.apply(lambda x: x.max() - x.min())   # 每列极差

生成器yield 逐个吐值,不占整块内存——处理大文件/大序列的利器:

def squares(n):
    for i in range(n):
        yield i ** 2
gen = squares(10)
sum(gen)          # 285,惰性求值
sum(x ** 2 for x in range(10))   # 生成器表达式:小括号版推导式

异常处理:数据分析脚本要敢于捕获、优雅降级:

try:
    float("abc")
except (TypeError, ValueError) as e:
    print(f"转不动:{e}")
finally:
    print("无论如何都执行(常用于关资源)")

📂 文件与操作系统

path = "data.txt"
with open(path, encoding="utf-8") as f:
    lines = [line.rstrip() for line in f]   # with 块结束自动关闭

⚠️ 两个坑:一,永远用 with 打开文件,忘记 close 的文件句柄会泄漏;二,永远显式写 encoding="utf-8"——Windows 默认 GBK,中文文件一不留神就乱码。二进制文件用 "rb" 模式,得到的是 bytes 对象。

📌 本章小结:tuple 不可变可解包、list 是切片主场、dict 是 O(1) 查找、set 管去重和集合运算;推导式要写到手熟;函数是对象、lambda 是 pandas 常客、yield 省内存;文件永远 with open(..., encoding="utf-8")。内功齐了,下一章正式进入 NumPy。