列表推导式与常见简写 (List Comprehension & Idioms)
1. 定义
这篇拆解几个 Python/数据分析里高频出现、初看有点绕的”一行流”写法:
- 列表推导式 (list comprehension):一行生成列表
- 条件表达式 (三元运算符 ternary):
A if 条件 else B - 两者组合:推导式里嵌条件表达式
- 列表拼接 + pandas 选列:
df[cols + ['label']]
核心思想:把”建一个空列表 → for 循环 → append”压缩成一行,更简洁也更快。
2. 列表推导式 (List Comprehension)
# 传统写法
result = []
for x in X_train:
result.append(x * 2)
# 推导式写法(等价,一行)
result = [x * 2 for x in X_train]结构:[对每个元素做什么 for 变量 in 可迭代对象]
带过滤:
[x for x in nums if x > 0] # 只保留 >0 的(末尾 if 是"过滤")3. 条件表达式(三元运算符)
# 传统写法
if score >= 60:
label = "pass"
else:
label = "fail"
# 三元写法(一行)
label = "pass" if score >= 60 else "fail"结构:值A if 条件 else 值B —— 条件成立取 A,否则取 B。
注意区分两种 if 的位置:
A if 条件 else B在 for 前面 → 是”选值”(三元表达式,必须有 else)... for x in xs if 条件在 for 后面 → 是”过滤”(筛选元素,没有 else)
4. 组合拆解:[1 if np.dot(w, x) + t >= 0 else -1 for x in X_train]
这是感知机 / 线性分类器做预测的经典写法。逐层拆开:
predictions = [1 if np.dot(w, x) + t >= 0 else -1 for x in X_train]等价的展开写法:
predictions = []
for x in X_train: # ③ 遍历每个样本 x
score = np.dot(w, x) + t # ① w·x + t:加权求和 + 偏置
if score >= 0: # ② 判断符号
predictions.append(1) # ≥0 判为 +1 类
else:
predictions.append(-1) # <0 判为 -1 类逐部分含义:
| 片段 | 含义 |
|---|---|
np.dot(w, x) | 权重向量 w 和样本 x 的点积(加权求和),见 matrix-operations.md |
+ t | 加偏置项 (bias/threshold) |
>= 0 | 看结果符号,决定分到哪一类 |
1 if ... else -1 | 三元表达式:≥0 → 1,否则 → -1 |
for x in X_train | 对训练集每个样本都算一遍 |
[... ] | 把每个样本的预测收集成一个列表 |
一句话:对每个样本算
w·x + t,≥0 判 +1、<0 判 -1,结果收成一个列表。 这就是感知机的预测规则。
向量化版本(更快,避免 Python 循环):
scores = X_train @ w + t # 一次算完所有样本(矩阵乘 + 广播) predictions = np.where(scores >= 0, 1, -1) # 逐元素三元
np.where(条件, A, B)就是 NumPy 版的”三元表达式”,作用在整个数组上。见broadcasting.md。
5. 列表拼接 + pandas 选列:virginica = virginica[features + ['label']]
features = ["sepal_length", "sepal_width", "petal_length", "petal_width"]
virginica = virginica[features + ["label"]]拆两步看:
第一步:features + ['label'] 是列表拼接
["sepal_length", "sepal_width", "petal_length", "petal_width"] + ["label"]
# = ["sepal_length", "sepal_width", "petal_length", "petal_width", "label"]
+对列表是拼接(不是逐元素相加,那是 NumPy 的行为)。这里把特征列名列表末尾加上"label"列名。
第二步:df[列名列表] 是”按列子集化”
virginica[["sepal_length", ..., "label"]] # 传一个列名 list,取这几列组成新 DataFrame关键区别:
df["label"]传单个字符串 → 返回一列 Seriesdf[["label"]]或df[cols]传列表 → 返回 DataFrame(哪怕只有一列)
合起来的意思:从 virginica 这张表里,只保留 4 个特征列 + label 列,丢掉其他列,重新赋值回去。 数据清洗里挑选需要的列时超常用。
6. 常见坑
- 三元表达式漏了
else:x if cond(放 for 前面)语法错误,选值必须有 else;只有当过滤(放 for 后面)时才不用 else - 两种 if 位置搞混:
[x for x in xs if cond]是过滤;[a if cond else b for x in xs]是每个都映射成 a 或 b,元素个数不变 - 列表
+当成逐元素加:[1,2]+[3]是[1,2,3](拼接);想逐元素加要用 NumPy 数组 - 选列用了单括号还是双括号:
df['a']出 Series,df[['a']]出 DataFrame,下游代码期望哪种要对上 - 选列的列名拼错/不存在:
df[cols]里有不存在的列名会KeyError - 推导式套太多层:三层以上的嵌套推导式可读性差,不如老老实实写 for 循环
7. 延伸阅读 / 关联概念
- lambda — 另一种常和推导式/
map/filter搭配的简写;见lambda.md - 矩阵运算 / 点积 —
np.dot(w, x)的数学含义;见matrix-operations.md - 广播 /
np.where— 把三元逻辑向量化到整个数组;见broadcasting.md - pandas 选列 vs loc/iloc —
df[cols]选列,行列都要选时用loc;见pandas-loc-iloc.md - Series vs DataFrame — 单括号/双括号返回类型的区别;见
numpy-pandas-datatypes.md - 官方文档:List Comprehensions