在Python中,for循环是一项强大的工具,不仅可以用于遍历序列,还能在各种场景中展现出其灵活性和实用性。本文将深入探讨for循环的多重妙用,通过丰富的示例代码,让读者更全面地了解和掌握for循环的各种技巧和应用。
遍历序列
for循环最常见的用途之一是遍历序列,例如列表、元组或字符串。
fruits = ["apple", "banana", "orange"]
for fruit in fruits:
print(fruit)
这简洁的语法使得遍历序列变得轻而易举,而在这之上,我们可以构建更为复杂的操作。
枚举迭代
有时候,不仅需要获取元素的值,还需要知道元素所在的位置。enumerate() 函数是一个强大的工具,可以在for循环中轻松实现这一需求。
fruits = ["apple", "banana", "orange"]
for index, fruit in enumerate(fruits):
print(f"Index: {index}, Fruit: {fruit}")
这种方式可以同时获取索引和元素值,对于迭代中的元素位置敏感的操作非常有用。
遍历字典
for循环不仅限于遍历序列,还可以用于遍历字典的键、值或键值对。
遍历键值对
student_scores = {"Alice": 85, "Bob": 92, "Charlie": 78}
for name, score in student_scores.items():
print(f"{name}'s score: {score}")
遍历键
for name in student_scores.keys():
print(f"Student: {name}")
遍历值
for score in student_scores.values():
print(f"Score: {score}")
通过这些方法,能够更加灵活地处理字典中的数据。
使用 break 和 continue
for循环中的 break 和 continue 语句可以用于更精细地控制循环的执行流程。
使用 break 提前结束循环
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for num in numbers:
if num > 5:
break
print(num)
使用 continue 跳过当前循环
for num in numbers:
if num % 2 == 0:
continue
print(num)
这两个语句的灵活运用可以更好地掌控循环的执行过程。
循环中的 else 语句
for循环还支持 else 语句,它会在循环正常执行完毕时执行,但在循环中遇到 break 语句时不执行。
for num in numbers:
if num == 11:
print("Number found!")
break
else:
print("Number not found.")
这种结构可以方便地判断循环是否因为 break 而提前结束。
列表推导式
for 循环可以与列表推导式结合,简化代码逻辑,快速生成列表。
squares = [x**2 for x in range(1, 6)]
print(squares) # 输出: [1, 4, 9, 16, 25]
多重循环
for 循环可以嵌套,实现多重循环,用于处理多维数据。
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
for row in matrix:
for element in row:
print(element)
使用 zip 迭代多个序列
zip 函数是另一个能够发挥 for 循环威力的工具,它能够同时迭代多个序列。
names = ["Alice", "Bob", "Charlie"]
ages = [25, 30, 22]
for name, age in zip(names, ages):
print(f"{name} is {age} years old.")
zip 函数将多个序列打包成一个元组,然后通过 for 循环轻松地进行迭代,这在处理多个相关联的数据时非常方便。
for-else 语句的应用
for-else 结构不仅仅局限于列表中元素是否存在的判断,还可以应用于其他场景。
for i in range(5):
print(i)
else:
print("Loop finished.")
这种结构能够在循环完成后执行一些特定的代码,而不必在循环体外引入额外的变量。
自定义可迭代对象
通过实现 iter 和 next 方法,可以创建自定义的可迭代对象,使其可以被 for 循环使用。
class Squares:
def __init__(self, n):
self.n = n
self.i = 0
def __iter__(self):
return self
def __next__(self):
if self.i >= self.n:
raise StopIteration
result = self.i ** 2
self.i += 1
return result
squares = Squares(5)
for square in squares:
print(square)
这样的自定义迭代器可以在实现特殊需求时发挥重要作用,增加了代码的灵活性。
异步迭代
在处理异步编程时,for 循环也有其独特的应用,例如使用 asyncio 库进行异步迭代。
import asyncio
async def async_generator():
for i in range(5):
yield i
await asyncio.sleep(1)
async def main():
async for num in async_generator():
print(num)
await main()
这展示了 for 循环在异步编程中的适用性。
决策树算法如何工作
套用西瓜书上的一个图来说明决策树算法是如何工作的:
我们挑选西瓜时,都会考虑西瓜脐部、色泽、根蒂以及敲一敲听声音等因素(特征),决策树就是对这些考虑因素进行逐个拆解,从而判断西瓜(样本)是好瓜还是坏瓜(类别)。
从上面来看,这些特征好像都是离散型的,对于 Iris 数据集中数值特征来说,我们可以设定一个阈值,比如判断萼片宽度(sepal width)是否小于 2.5 厘米。
决策树算法从树根开始,选择能够产生最大信息增益(Information Gain,IG)的特征进行数据集拆分,一直到叶子节点为止,所有叶子节点中的样本都属于同一个类别,这样就可能会产生非常深的树,从而引发过拟合问题,所以就需要对树进行剪枝以限制树的深度(模型复杂度)。
最大化信息增益
信息增益的公式定义如下
f 是要执行拆分的特征,Dp 和 Dj 表示父节点 p 和第 j 个孩子节点中的样本集,Np 和 Nj 分别表示父节点 p 和第 j 个孩子节点中的训练样本数量。I 就表示节点的纯度。
所以信息增益就是衡量父节点纯度和所有孩子节点纯度加权和的差异。
包括 scikit-learn 在内的大多数机器学习库的决策树算法都会将父节点分裂成左右两个孩子节点,所以信息增益公式可以简化为:
三种纯度衡量指标
现在有 Gini 纯度、熵和分类错误三种节点纯度衡量指标。
首先我们看一下熵(entropy):
p(i|t) 表示节点 t 中属于类别 i 的样本占该节点中所有样本的比例。如果节点 t 中所有样本都属于同一个类别,那么熵就是 0,表示这个节点没有不确定性;如果节点 t 中的每个样本都分属于不同的类别,那么此时熵最大,表示这个节点的不确定性最大。
Gini 纯度可以看作是最小化误分类概率的指标:
在实际应用中,Gini 纯度和熵表现很类似,所以不建议花很多精力去比较在选择哪种纯度衡量指标。相反更应集中精力实验不同决策树剪枝技巧。
最后一个就是分类错误:
这个指标适合用来做决策树剪枝,但是由于它对于节点中类别概率分布不敏感,所以它不适合用来生成决策树。
生成决策树
我们现在使用 scikit-learn 提供的 DecisionTreeClassifier 构建一个深度为 4,采样 Gnini 纯度的分类决策树,还是使用 Iris 数据集。
决策树算法不要求特征缩放。
可以看到这些决策边界几乎和坐标轴平行。
我们可以可视化生成的决策树,从而也能对模型的预测结果做出解释。
树分支的左孩子表示满足父节点中的判断条件,右孩子表示不满足条件。